# OCPP

> Reference for the OCPP driver: the embedded central system, charge point certificates, connector tags and the Meter/Status 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.

## 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 the application's own OPC UA
certificate. Every charge point must present a **client certificate**:

1. It must build a valid chain on this Windows machine, with no TLS policy errors;
   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.

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

## 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, the device
reports that nothing has been observed rather than going offline, and no failure is
journaled: this is the normal state of a charge point that was just configured and
has not arrived.

## Tags {#tags}

An OCPP tag reads one **connector** of the charge point (connector in the OCPP
sense: one plug position, numbered from 1). Two 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). 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`, `Charging`, `Faulted`). | String |

A name with neither suffix is refused by validation: name tags like
`Connector1Meter` and `Connector1Status`.

| Field | What it does | Values | Default |
|---|---|---|---|
| Connector | The connector number on the charge point this tag reads. | 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
anything that can call a method can issue one: an OPC UA client, or a Logic action
that calls 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. |
| 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`.

**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` and one
`Status` 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`.
