How Flowion maps onto OCPP

See where the protocol ends and Flowion's model begins: how three OCPP versions become one API, and how each translation is handled.

4 min readEdit this page
On this page

Flowion's API is built so that you never handle OCPP. This page shows the seam: what belongs to the protocol, what Flowion adds above it, and how the translation between the two is done.

The dividing line

Everything a charging station knows about is OCPP's. Everything above it is Flowion's.

Organization               ─┐
 └─ Project                 │  Flowion's: accounts, isolation, sites,
     ├─ Site                │  electrical topology, live metering.
     │   └─ Circuit         │  OCPP has no idea these exist.
     │       └─ Monitor    ─┘
     └─ Charger            ─┐
         └─ EVSE            │  OCPP's: the three-tier hardware model,
             └─ Connector   │  transactions, cards, meter readings.
                           ─┘

The charger is where the two meet. Above it, concepts exist because operators need them; below it, because the specification defines them.

Flowion conceptWhat it gives you
OrganizationYour account, billing and membership.
ProjectThe isolation boundary: one per brand, client or country.
SiteA physical location (a location in the API). OCPP has a Charging Location but gives it no protocol meaning.
CircuitThe wiring chargers hang off, as a tree. What load management works from.
Energy monitorLive load on a circuit, from a meter beside the chargers.

Core concepts covers these in full.

You ask for an outcome, Flowion picks the message

The API never names an OCPP message. You will not find StartTransaction or TransactionEvent in it. You ask for an outcome, and Flowion works out what goes on the wire for that charger:

You ask forFlowion sends
Start or stop a session, release a cable, restartThe command for the charger's version, queued until it is reachable
A setting, such as light intensityA 1.6 configuration key or a 2.x device-model variable, and keeps it there
Who may charge, and offline behaviourThe station's authorization settings and, on 1.6, its local list of cards
A current limit on a chargerA charging profile
A share of a site's capacityCurrent limits, updated as load changes
A message on the charger's screenDisplay messages, re-sent if the charger loses them

Several of these are desired state, not one-off commands. You say what a charger should hold; Flowion compares that with what the charger reports and corrects any drift it finds.

The result: one integration for a mixed fleet. A 1.6 charger and a 2.1 charger are the same kind of object to your code.

One model, shaped like OCPP 2.x

Flowion's model follows 2.x's structure, and 1.6 is mapped into it. That direction is deliberate: wherever the two differ, 1.6's shape is a special case of 2.x's, so mapping upward only ever adds structure and never throws any away.

For a 1.6 charger, that means:

Each connector becomes an EVSE. 1.6 has no EVSE tier, so connector n becomes EVSE n, holding one connector. Your code walks the same hierarchy for every charger.

Status is split in two. 1.6's single connector status mixes availability with what the outlet is doing. Flowion stores 2.x's split: a connector status, and a charging state on the session.

The transaction becomes an event log. StartTransaction, MeterValues and StopTransaction become Started, Updated and Ended events on one session, the same shape a 2.x charger produces.

Meter start and stop become readings. There are no meterStart or meterStop fields in Flowion's model. 1.6's two numbers become ordinary energy readings at the start and end of the session, which is what 2.x sends, so energy is worked out the same way for every charger.

Two identities per object

Every object has a Flowion UUID, and many also carry the identifier the protocol uses on the wire. Sessions show why both matter:

VersionWho assigns the transaction idTypeAPI field
1.6the CSMS, in its answer to the startintegernumericId
2.xthe charging station, from the first eventstring, up to 36 charstextId

A 2.x charger may pick the string "90210", which is not the same as a 1.6 integer 90210. Flowion keeps the two apart, so an id from one version can never be mistaken for the other.

Key on Flowion's UUIDs. They are stable and the same shape for every charger. The protocol ids are there for matching against a charger's own screen or logs.

When a charger cannot carry a request

Flowion's model is wider than any single protocol version. When you ask for something a charger's version cannot express, Flowion refuses with an error that says so, rather than sending something close:

RequestWhy a 1.6 charger cannot take it
Stop a session identified by a string id1.6 transaction ids are integers
Release the cable on connector 0On 1.6, connector 0 is the whole station
Show a message on the screen1.6 has no display messages

Check ahead with GET .../commands/capabilities, which the console also uses. Which OCPP version am I dealing with? lists the features tied to one version.

Values that change on the way in

A few 1.6 values have no exact 2.x equivalent. Each is mapped on purpose:

1.6 valueBecomesNote
Stop reason HardResetImmediateReset2.x's name for the same thing
Stop reason SoftResetRebootReads the same as 1.6's own Reboot
Stop reason UnlockCommandOther2.x has no unlock stop reason
Measurand Temperature, RPMCharger metricsKept as diagnostic readings, not on the session
Signed meter dataKept as receivedStored as the signed blob, not as a number
No stop reasonLocalThe only case both versions allow it to be left out

The other direction narrows too. Flowion can reach verdicts on a card that 1.6 has no word for, such as "not at this location" or "not at this time". Those reach a 1.6 charger as Blocked, while Flowion records the real reason, so your records say why even when the charger could not be told.

Fields only 2.x chargers fill in

These stay empty for a 1.6 charger rather than being guessed, so "the charger did not say" is always distinguishable from a real value:

  • Card type. 1.6 never says whether a card was RFID or anything else.

  • Trigger reason. 2.x says why it sent each transaction event.

  • Remote start link. 2.x sessions carry the remoteStartId of the remote start that began them.

  • Offline flag. 1.6 cannot say "this happened while I was disconnected", so it always reads false.

  • Sequence numbers. A 2.x charger numbers its events, which reveals a gap. For 1.6, Flowion numbers them in arrival order, which keeps them ordered.

What Flowion adds on top

Some of the most useful records have no OCPP message at all. Flowion keeps them for every charger:

  • Boot history, so a firmware change or a restart loop shows up.

  • Registration progress, step by step, for a charger being set up.

  • Command history, with each command's status and the charger's answer.

  • Every inbound message the charger sent, for when you need the raw detail.

  • Status history and active faults per charger and outlet.

Where to go next