# OCPP

> Reference for the OCPP driver: the embedded central system, charge point certificates, connector tags and the Meter, Status and Transaction naming rule.

The OCPP driver makes Ganter Lab the **central system** for EV charge points
speaking the Open Charge Point Protocol. As with LoRaWAN, the direction is
reversed: the station does not connect to the charger; the **charge point connects
to Ganter Lab** over a WebSocket, registers itself, and pushes its telemetry in.

The embedded central system starts when the first enabled OCPP device is loaded
into the running connector, and stops when the last one is removed or disabled: with
no charge point configured, nothing listens on the port at all. Adding one brings it
back, without restarting the application. Each Device you configure represents **one
charge point**, identified by the id it connects under.

## Prepare the charge point {#prepare-the-charge-point}

The charge point dials this station, and out of the box it does not know where the
station is. The page of each OCPP device carries a **Prepare the charge point** card
with what to enter in the charge point's OCPP settings, written for this station:

| What to configure on the charge point | What the card gives |
|---|---|
| Central system address | `wss://<address>:19521/<charge-point-id>`, first with the address by this station's name (the "Network name" of the embedded OPC UA server's configuration, or the computer's name while it is empty), then once for each IPv4 address this station has on a network, each with a copy button. A charge point that checks the server's name needs the address by name; one that does not accepts any of them, and then the one on its own network will do. |
| Charge point identity | The device's **Identifier**, or, while it is empty, the name the station gives the device. Many charge points add their identity to the address themselves: on those, enter the address only up to the port, and the identity in its own field. |
| Security | TLS with a client certificate, which OCPP calls security profile 3. Profiles 1 and 2, which have no client certificate, are not accepted. The charge point's certificate names its identity in its common name or in a DNS alternative name (see [Certificates](#certificates)). |
| Root certificate of the central system | The whole path of `GanterLabConnector-<station>.pem`, this station's root certificate, which the charge point installs to trust the central system, with an **Open folder** button that shows the file in Windows' file manager. The button works in the station's own window; in a browser on another device it stays greyed and says so. |

The card is open while the charge point has not connected since the station opened,
and folds once it has: it stays on the page for the next time the charge point is
set up. A charge point whose certificate no root installed in Windows vouches for is
refused at its first connection and waits under [Equipment access](#equipment-access).

The file is written the first time the central system starts, once a charge point is
registered, and until then the card says so. It is the same file for every charge point
of this station, and it does not change when the station's name or addresses do (see
[The station's root certificate](#the-stations-root-certificate)).

**Every charge point that was given a file by an earlier version needs this one,
installed once.** On the first start of this version, a station whose charge points may
hold an earlier file shows a notice on the card, **This station's file changed**, in that
run only. The notice is a reminder, not the instruction: install the new file once on
every charge point of a station that already had charge points, whether or not the card
shows the notice. A charge point that checks the server's name accepts the station from
then on.

## Central system and ports {#central-system-and-ports}

| What | Value |
|---|---|
| Protocol versions | OCPP **1.6**, **2.0**, **2.0.1** and **2.1** (WebSocket subprotocol negotiation; JSON) |
| Listener | All interfaces, TCP port **19521**, TLS only |
| Charge point address | `wss://<station-address>:19521/<charge-point-id>` |
| Identity | The last part of the WebSocket path is the charge point id |

The listener is always TLS; a plain-HTTP listener is refused anywhere except
loopback, which exists only for in-process tests. A charge point registers itself
with its BootNotification (answered with a 60-second heartbeat interval), and every
badge presented for authorization is accepted: Ganter Lab observes charging, it
does not manage tariffs or access.

## Certificates {#certificates}

The server side of the TLS handshake presents a certificate signed by this station's
own root (see [The station's root certificate](#the-stations-root-certificate)). Every
charge point must present a **client certificate**:

1. It must be trusted in one of two ways: it builds a valid chain on this Windows
   machine, up to a root installed there, with no TLS policy errors; or you approved
   this very certificate under **Equipment access** on this driver's page (below).
   Either way it must be within its validity dates. A failed check refuses the
   connection and the journal records which check failed.
2. Its subject common name or a DNS subject alternative name must **name the charge
   point id** it connects under. A certificate naming a different charge point than
   the one in the connection path is refused, even an approved one.

A handshake without a client certificate is dropped before any data flows.

### The station's root certificate {#the-stations-root-certificate}

| What | Value |
|---|---|
| The root | Created once for the profile, the first time the central system or the LoRaWAN network server starts. Its subject is `Ganter Lab Connector (<station>)`, where `<station>` is the network name on that day. It never expires and is never replaced on its own: renaming the computer, changing the network name or changing the station's addresses leaves it as it is. |
| The file | `GanterLabConnector-<station>.pem`, the public part only, in the data folder under `ua\pki\station`. It is the same file for a charge point and for a LoRaWAN gateway, which installs it as `tc.trust`. |
| The certificate the central system presents | Signed by that root. It names the station's network name (the "Network name" of the embedded OPC UA server's configuration; the computer's name while it is empty), `localhost` and every IPv4 address of the station, so a charge point that checks the server's name (security profile 3) accepts it. When the network name or an address changes, only this certificate is issued again, by itself, with nothing to do on the charge point. |
| The OPC UA server's certificate | Not used here. The embedded OPC UA server keeps its own certificate for OPC UA clients. |

A root that is stored but can no longer be opened, for example because it was written by
another Windows account, is replaced by a new one. Every charge point then needs the new
file, once, and the card says so.

### Equipment access {#equipment-access}

A charge point whose certificate no root installed in Windows vouches for, because it
is self-signed or issued by the maker's own authority, does not need that root
installed on this computer. Its first connection is refused, and its certificate waits
under **Equipment access** at the end of the OCPP driver's page, the same card the
OPC UA driver's page carries, until you decide:

| Group | What it lists | What you can do |
|---|---|---|
| Waiting for approval | Each refused certificate, under the charge point id it connected with: its SHA-256 fingerprint, when it last tried and how many times, and its details (subject, issuer, domain names, validity dates). | **Approve** admits the certificate from the charge point's next connection. **Block** keeps refusing it without listing it here again. **Discard all pending certificates** empties the list without approving or blocking anything. |
| Approved equipment | The certificates you approved or imported, with who decided and when. | **Withdraw approval** closes the charge point's connection and returns its certificate to the list. **Import certificate** approves a charge point's public certificate before its first connection. |
| Blocked equipment | The certificates you blocked. A blocked certificate is refused even if Windows trusts it. | **Unblock** returns the certificate to the list, without approving it. |

Each certificate is approved on its own, never the authority that signed it, so a
second charge point from the same maker waits for its own approval. Check the
fingerprint against the one the charge point reports before you approve it. Approving
does not change which charge point the certificate is: it is still admitted only under
the charge point id it names, and only within its validity dates. A certificate Windows
already trusts is admitted as before and never appears here; nothing is approved
automatically.

The decisions are kept in the station's data folder, under `ua\pki\ocpp`, and survive a
restart. Only a role that may configure the Connector is drawn the verbs, and they act
only in the station's own window: a browser on another device reads the card without
changing it, and no agent tool approves a certificate. The list holds up to 100
certificates waiting for approval; once it is full, a new one is still refused but not
listed, and the card says so.

## Connection fields {#connection-fields}

| Field | What it is | Format | Default |
|---|---|---|---|
| Identifier | The **charge point id** this Device stands for: the same id the charger puts at the end of its connection URL and carries in its certificate. | Free text, matched ignoring letter case. Empty falls back to the sanitized device name. | empty |

There are no host, port or credential fields: the charger holds the connection
details, and this station only needs to know which charge point the Device means.
There are also no poll-interval fields. OCPP is push-based (the charge point sends
MeterValues and StatusNotification messages on its own schedule), and the tags
refresh from the last received data on a fixed one-second cycle.

A charge point that has not connected yet, or a connector that has not reported yet,
therefore has **no reading**, not a failed one. Its tags stay empty and no failure is
journaled: a point the charge point has not sent yet is the protocol working.

### The charge point's health {#the-charge-points-health}

Since a charge point sends its points when it has them, the silence of its tags cannot
tell one that has not reported yet from one that is gone. Its health, the dot on its row
in the tree and the **Health** column on the driver's page, follows its connection to
this station's central system instead:

| Situation | Health |
|---|---|
| Configured, and it has not connected since the station opened | **Offline** |
| Connected, nothing sent yet | **Online** |
| Connected and sending | **Online**, or **Partly online** when some of the points it sent fail to read |
| It dropped: stopped, unplugged or cut off from the network | **Offline**, until it connects again |
| The central system not listening | **Offline** |
| Disabled | **Disabled** |

While the charge point is connected, a connector it has not reported yet does not count
against it. For up to ten seconds after a change to the charge point is saved, or the
Connector starts, it reads **Not known yet**: putting the device back can restart the
central system, and the charge point takes a moment to connect again.

## Tags {#tags}

An OCPP tag reads one **connector** of the charge point (connector in the OCPP
sense: one plug position, numbered from 1). On OCPP 2.0, 2.0.1 and 2.1 the number is the **EVSE**'s instead,
since connectors there are numbered from 1 again inside each EVSE. Three kinds of tag exist, told apart by
the **tag name suffix**, which is validated:

| Tag name ends in | What it reads | Sensible type |
|---|---|---|
| `Meter` | The most recent sampled meter value the connector reported (OCPP MeterValues; on OCPP 2.x also the values a TransactionEvent carries during a session). The unit is whatever the charger sends, typically Wh. The server keeps the last 10 samples and the tag reads the newest. A connector that has never reported reads as an empty value, not as zero. | Float or Double |
| `Status` | The connector state text from the last StatusNotification (for example `Available`, `Faulted`). On OCPP 2.x an EVSE with more than one connector reads as the most telling of them: `Faulted` wins over `Occupied`, `Occupied` over `Reserved`, `Reserved` over `Available`, and `Available` over `Unavailable`. One that has not reported a status yet reads as an empty value. | String |
| `Transaction` | The number of the transaction running on the connector, which the RemoteStopTransaction and GetTransactionStatus commands take: on OCPP 1.6 the number this station handed out when the transaction started, on OCPP 2.x the charging station's own identifier, exactly as it sent it. It is filled when a transaction starts and emptied when it ends, so with none running it reads as empty text. Until a transaction starts or ends after the charge point connects it reads as an empty value, since one may already have been running. | String |

A name with none of these suffixes is refused by validation: name tags like
`Connector1Meter`, `Connector1Status` and `Connector1Transaction`.

| Field | What it does | Values | Default |
|---|---|---|---|
| Connector | The number this tag reads: the connector on OCPP 1.6, the EVSE on OCPP 2.x. | 1 or greater | 1 |
| Data type | The OPC UA type the value is exposed as. | Int32, Float, Double, String | Float |

OCPP tags are **read only**: the access field offers no writable choice, and a
write is refused rather than faked. Commanding a charge point is a command of its
own, described next, not a value written to a tag.

## Commands {#commands}

A charge point is commanded as well as read. Each OCPP Device carries the commands
below as **methods on the device** in this station's own OPC UA address space, so
an OPC UA client connected to this station issues one by calling it.

Every command takes the **charge point id** as its first argument, and hands back the
charge point's own answer as its result.

| Command | Further arguments | What it does |
|---|---|---|
| ChangeAvailability | ConnectorId, Type (`Operative` or `Inoperative`) | Takes a connector in or out of service. |
| ChangeConfiguration | Key, Value | Sets one configuration key on the charge point (OCPP 1.6). |
| ClearCache | none | Clears the charge point's authorization cache. |
| GetConfiguration | Key | Reads one configuration key (OCPP 1.6). |
| RemoteStartTransaction | IdTag | Starts a charging session for a token. |
| RemoteStopTransaction | TransactionId | Stops a running session. |
| GetTransactionStatus | TransactionId | Asks whether a transaction is still running (OCPP 2.x). |
| Reset | none | Restarts the charge point. |
| UnlockConnector | ConnectorId | Releases the cable from a connector. |
| SetChargingProfile | ConnectorId, Limit, NumberOfPhases | Sets a charging power limit, in watts. ConnectorId 0 limits the whole charge point; another number sets the default limit for the sessions that connector charges. |
| SetVariables | ComponentName, VariableName, AttributeType, AttributeValue | Sets one variable of one component (OCPP 2.x). |
| GetVariables | ComponentName, VariableName, AttributeType | Reads one variable of one component (OCPP 2.x). |

`AttributeType` is one of `Actual`, `Target`, `MinSet`, `MaxSet`.

**Where the TransactionId comes from.** RemoteStopTransaction and GetTransactionStatus act on one transaction, named by its number. Read it from the `Transaction` tag of the connector the session charges on (the EVSE on OCPP 2.x), for example `Connector1Transaction`, and pass it on as it reads. Empty text there means no transaction is running on that connector.

**One catalogue, every version.** The commands keep the names above whatever version the
charge point speaks, and the station sends each one in the form that version defines. In
OCPP 2.0, 2.0.1 and 2.1 connectors are grouped under EVSEs, and the number given as
ConnectorId names the EVSE.

| Command | On OCPP 1.6 | On OCPP 2.0, 2.0.1 and 2.1 |
|---|---|---|
| ChangeAvailability | Acts on the connector given; 0 is the whole charge point. | Acts on the EVSE given; 0 is the whole charging station. |
| RemoteStartTransaction | Sends the IdTag, and the charge point picks the connector. | Goes out as RequestStartTransaction, with the IdTag as a central token and a start id of its own; the charging station picks the EVSE. |
| RemoteStopTransaction | TransactionId is the number this station gave the session when it started. | Goes out as RequestStopTransaction; TransactionId is the charging station's own identifier, passed on as text. |
| Reset | A hard reset. | An immediate reset. |
| UnlockConnector | Releases the connector given. | Releases the first connector of the EVSE given. |
| SetChargingProfile | 0 sets the charge point's maximum profile; another connector gets a default profile. | 0 sets the charging station's maximum profile; another EVSE gets a default profile. |
| ChangeConfiguration, GetConfiguration | As in the table above. | Not part of these versions: the charging station answers with an error, and the command fails. |
| GetTransactionStatus, SetVariables, GetVariables | Not part of this version: the charge point answers with an error, and the command fails. | As in the table above. |

**Waiting for the answer.** A command is not finished when the station has sent it.
The station holds the call open until the charge point answers the very order it
sent, matched by the correlation identifier that travels with it, and only then
reports success and hands back the answer. Three things end it otherwise, each
reported as a failure with its reason:

- The charge point is not connected, or its connection is not open. Nothing is sent.
- The charge point answers with an OCPP error, for example a command it does not
  implement. The error code is the reason.
- **30 seconds** pass with no answer at all. A command nobody answered is a failure,
  never a success.

A command issued for a charge point that a Device does not carry, or that is not
connected, is refused straight away rather than queued.

## Discovery {#discovery}

**Discover** on the OCPP driver lists the charge points **currently connected** to
the central system, and reading a candidate's channels proposes one `Meter`, one
`Status` and one `Transaction` tag per connector the charge point has reported so far.

The **first charge point is always configured by hand**. Nothing can connect before
the central system listens, and it listens only once an OCPP Device exists here, so a
scan of an installation with no charge point configured finds nothing and says so.
Add that first Device with the id the charger will connect under; after that, a
charger that has connected can be discovered. The general discovery flow is described
on the [Connector](connector) page.

The id is matched **ignoring letter case**, in the certificate, in the connection
path and against the Device: a charger that connects as `cp-a` with a certificate
naming `CP-A` is the same charge point as a Device whose Identifier reads `CP-A`.

A charge point you already have reads its connectors again from its own page: **Discover tags**,
on the head of its Tags card, offers the `Meter`, `Status` and `Transaction` tags of each
connector it has reported while connected that the device has no tag for yet (see
[Discovering a device's tags](connector-discovery#discovering-a-devices-tags)).
