Skip to main content

New ResourceGet the complete CPO Rollout Blueprint and evaluate infrastructure migrations without lock-in risk.

Read the blueprint
engineeringFebruary 25, 2026

OCPP 2.0.1 Device Model: Components & Variables

OCPP 2.0.1 device model explained: the component-variable tree replacing 1.6 flat keys, with 4 attribute types and a 6-point checklist to keep fleets stable.

At a glance

The OCPP 2.0.1 device model is not just a cleaner schema. It changes how teams discover charger capabilities, manage configuration safely, and normalize mixed fleets during rollout.

CSMS and backend engineersCPO integration leadsTeams planning OCPP 2.0.1 rollout
  • Treat the device model as a rollout dependency, not only a protocol detail.
  • Inventory coverage, vendor-specific components, and read-write rights need validation early.
  • Mixed 1.6 and 2.0.1 fleets require a normalized internal model if you want stable operations.
  • Monitoring and report volume can become operational issues if you test only against ideal payloads.
Y
Yacine El Azrak
Co-founder & CEO
13 min read

What does the OCPP 2.0.1 device model change for deployers?

The OCPP 2.0.1 device model replaces the protocol's flat configuration store with a structured tree of components and variables, and that shift is one of the first places where "spec support" and "deployment readiness" diverge in production.

If you are implementing OCPP 2.0.1 in production, the device model is where vendor differences surface fastest. A charger can pass certification and still expose an inventory your backend cannot fully ingest, normalize, or write back safely.

For deployers, the real question is not whether a charger can technically answer GetVariables. It is whether your stack can:

  • discover what the charger actually exposes
  • tell standard components from vendor-specific ones
  • write configuration safely without breaking field behavior
  • monitor the right variables without flooding operations
  • normalize 2.0.1 data into the same operational model as the rest of your fleet

If those pieces are weak, the device model becomes another source of rollout friction instead of the improvement it is supposed to be.

Why did OCPP 1.6's configuration model break down?

OCPP 1.6 modeled the entire charger as one flat list of string configuration keys, which works for a single-connector wallbox but collapses once a station has multiple EVSEs, connectors, and subsystems that each need their own addressable state.

In OCPP 1.6, charger configuration is a flat key-value store. You read and write configuration using GetConfiguration and ChangeConfiguration with keys like HeartbeatInterval, MeterValuesSampledData, or AuthorizeRemoteTxRequests.

This works fine for simple chargers. But modern charging stations are complex:

  • Multiple EVSEs with different capabilities
  • Multiple connectors per EVSE
  • Network interfaces (cellular, Wi-Fi, Ethernet)
  • Power electronics with independent control
  • Display screens, RFID readers, payment terminals

Representing all of this as flat key-value pairs leads to naming nightmares: ConnectorPhaseRotation.1.1, ChargingScheduleAllowedChargingRateUnit.1. There's no structure, no hierarchy, no way to discover what a charger supports.

OCPP 2.0.1's device model fixes this completely.

How does the component-variable architecture work?

The device model is a tree of components, each with variables that describe its state and configuration. The Open Charge Alliance defines this structure in the OCPP 2.0.1 specification set, so standard component and variable names are portable across compliant vendors.

The diagram below shows the shape every 2.0.1 charger follows. A root ChargingStation component holds station-level variables, then nests one component per EVSE, each of which nests its own Connector components. Cross-cutting subsystems like the communication controller, token reader, and display sit alongside the EVSE branch as their own components. Read it as an addressing scheme: the path to any value is component, then optional instance and EVSE, then variable.

┌──────────────────────────────────────────────────────────────┐
│                     ChargingStation                           │
│                     (Root component)                          │
│                                                              │
│  Variables:                                                  │
│  ├─ Available (bool)                                         │
│  ├─ Model (string)                                           │
│  ├─ VendorName (string)                                      │
│  ├─ FirmwareVersion (string)                                 │
│  └─ Power (decimal, watts)                                   │
│                                                              │
│  ┌─────────────────────────┐  ┌─────────────────────────┐   │
│  │         EVSE 1          │  │         EVSE 2          │   │
│  │                         │  │                         │   │
│  │  Variables:             │  │  Variables:             │   │
│  │  ├─ Available (bool)    │  │  ├─ Available (bool)    │   │
│  │  ├─ Power (decimal)     │  │  ├─ Power (decimal)     │   │
│  │  └─ SupplyPhases (int)  │  │  └─ SupplyPhases (int)  │   │
│  │                         │  │                         │   │
│  │  ┌──────────────────┐   │  │  ┌──────────────────┐   │   │
│  │  │   Connector 1    │   │  │  │   Connector 1    │   │   │
│  │  │                  │   │  │  │                  │   │   │
│  │  │  Variables:      │   │  │  │  Variables:      │   │   │
│  │  │  ├─ Available    │   │  │  │  ├─ Available    │   │   │
│  │  │  ├─ ConnectorType│   │  │  │  ├─ ConnectorType│   │   │
│  │  │  └─ SupplyPhases │   │  │  │  └─ SupplyPhases │   │   │
│  │  └──────────────────┘   │  │  └──────────────────┘   │   │
│  └─────────────────────────┘  └─────────────────────────┘   │
│                                                              │
│  ┌────────────────┐  ┌────────────────┐  ┌───────────────┐  │
│  │ Controller     │  │ TokenReader    │  │ Display       │  │
│  │                │  │                │  │               │  │
│  │ Variables:     │  │ Variables:     │  │ Variables:    │  │
│  │ ├─ Identity    │  │ ├─ Available   │  │ ├─ Available  │  │
│  │ ├─ Interval    │  │ └─ Type       │  │ └─ Language   │  │
│  │ └─ Retries     │  │   (RFID/NFC)  │  │               │  │
│  └────────────────┘  └────────────────┘  └───────────────┘  │
└──────────────────────────────────────────────────────────────┘

Every physical or logical part of the charger is a component. Every property of that component is a variable.

Components in detail

A component in OCPP 2.0.1 is addressed by up to three fields that together form a unique path to any physical or logical part of the charger. That precise addressing is what lets a backend target one connector on one EVSE without ambiguity.

A component is identified by three fields:

  • name — what it is (e.g., "EVSE", "Connector", "Controller")
  • instance — which one, if there are multiples (e.g., "1", "2")
  • evse — which EVSE it belongs to (for EVSE-scoped components)

This gives you a precise address for anything on the charger:

  Component: EVSE, instance: 1
  └─ Variable: Available → true

  Component: Connector, instance: 1, evse: {id: 1}
  └─ Variable: ConnectorType → "cType2"

  Component: Controller
  └─ Variable: HeartbeatInterval → 60

Standard components

OCPP 2.0.1 defines a set of standard components that all chargers should support:

ComponentDescription
ChargingStationThe station itself
EVSEEach charging point
ConnectorPhysical connector
ControllerOCPP communication controller
SecurityCtrlrSecurity settings
AuthCtrlrAuthorization settings
TxCtrlrTransaction settings
SampledDataCtrlrMeter value configuration
MonitoringCtrlrMonitoring and alerting
ClockCtrlrTime synchronization
DisplayMessageCtrlrOn-screen messages
LocalAuthListCtrlrLocal authorization list
SmartChargingCtrlrSmart charging capabilities
ReservationCtrlrReservation settings

Vendors can add custom components for hardware-specific features.

What are OCPP 2.0.1 variables and attribute types?

A single OCPP 2.0.1 variable carries up to four attribute types, so one name like Power holds the live measurement and its configured bounds at once. This separation is what lets you read what a charger is doing and write what it should do without overloading a single field.

The layout below shows the four attributes for Power on one EVSE. Actual is the read-only live reading. MaxSet and MinSet are the writable ceiling and floor. Target is the writable setpoint you request. The charger resolves the effective output by clamping Target between MinSet and MaxSet, while Actual keeps reporting the real measurement independently. In mixed-vendor fleets, the common rollout surprise is a variable that advertises a writable attribute but silently rejects or clamps it on the hardware.

┌─────────────────────────────────────────────────────┐
│  Variable: "Power" on EVSE 1                        │
│                                                     │
│  ┌──────────────┐  ┌───────────────┐               │
│  │   Actual      │  │   MaxSet      │               │
│  │   (read-only) │  │  (read-write) │               │
│  │               │  │               │               │
│  │  Current      │  │  Maximum      │               │
│  │  power draw:  │  │  allowed:     │               │
│  │  15,400 W     │  │  22,000 W     │               │
│  └──────────────┘  └───────────────┘               │
│                                                     │
│  ┌──────────────┐  ┌───────────────┐               │
│  │   MinSet      │  │   Target      │               │
│  │  (read-write) │  │  (read-write) │               │
│  │               │  │               │               │
│  │  Minimum      │  │  Desired      │               │
│  │  allowed:     │  │  setpoint:    │               │
│  │  6,000 W      │  │  11,000 W     │               │
│  └──────────────┘  └───────────────┘               │
│                                                     │
│  Effective power = MIN(MaxSet, Target) = 11,000 W   │
│  Constrained by MinSet: must be >= 6,000 W          │
│  Actual shows real-time measurement: 15,400 W       │
└─────────────────────────────────────────────────────┘

Actual

The current, real-time value. Read-only. Updated by the charger as conditions change.

Examples:

  • Power.Actual = 15400 (watts currently being drawn)
  • Temperature.Actual = 42 (degrees Celsius)
  • Available.Actual = true (connector is available)

MaxSet

The maximum allowed value. Can be set by the CSMS to impose limits.

Examples:

  • Power.MaxSet = 22000 (don't exceed 22 kW)
  • CurrentImport.MaxSet = 32 (max 32 amps)

MinSet

The minimum allowed value. Used to prevent settings below safe thresholds.

Examples:

  • Power.MinSet = 1380 (minimum charging power: 6A single phase)

Target

The desired setpoint. What you want the value to be.

Examples:

  • Power.Target = 11000 (we want this EVSE to deliver 11 kW)

How do you read the OCPP 2.0.1 device model?

OCPP 2.0.1 gives you two ways to read the device model: a targeted lookup with GetVariables for specific values, and a bulk inventory dump with GetBaseReport when you need the full picture. Most backends use both, targeted reads for live state and a full inventory on first connection.

GetVariables

Request specific variables by component and variable name:

Request:
  getVariableData: [
    { component: {name: "EVSE", evse: {id: 1}},
      variable: {name: "Power"},
      attributeType: "Actual" },
    { component: {name: "Controller"},
      variable: {name: "HeartbeatInterval"},
      attributeType: "Actual" }
  ]

Response:
  getVariableResult: [
    { attributeValue: "15400",
      attributeStatus: "Accepted" },
    { attributeValue: "60",
      attributeStatus: "Accepted" }
  ]

GetBaseReport

Request a complete dump of the device model:

Request:
  requestId: 1
  reportBase: "FullInventory"

The charger responds with multiple NotifyReport messages
containing ALL components and variables.

FullInventory returns everything. ConfigurationInventory returns only configurable variables. SummaryInventory returns a summary.

How do you write to the OCPP 2.0.1 device model?

Writing configuration uses SetVariables, which targets a specific component, variable, and attribute type, then returns a per-variable status. The status matters as much as the write itself: a RebootRequired or Rejected result is the difference between a config change that took effect and one that silently did not.

SetVariables

Change one or more variables:

Request:
  setVariableData: [
    { component: {name: "Controller"},
      variable: {name: "HeartbeatInterval"},
      attributeType: "Target",
      attributeValue: "30" }
  ]

Response:
  setVariableResult: [
    { attributeStatus: "Accepted" }
  ]

Possible responses: Accepted, Rejected, RebootRequired, NotSupportedAttributeType, UnknownComponent, UnknownVariable.

How does device model monitoring work?

The device model also supports monitoring: instead of polling, you tell the charger to push a NotifyEvent when a variable crosses a threshold, changes by a delta, or on a periodic schedule. This turns the backend from a poller into a listener for the variables that actually matter.

The example below sets an upper-threshold monitor on Temperature for one EVSE. When the live reading passes the configured limit, the charger emits a NotifyEvent carrying the component, variable, value, timestamp, and severity, so the backend has everything it needs to act without a follow-up read. The four monitor types, upper threshold, lower threshold, delta, and periodic, cover most operational alerting. The practical risk is over-subscribing: too many monitors at low severity generate event volume that buries the signals you care about.

┌─────────────────────────────────────────────────────┐
│  Monitor: Temperature on EVSE 1                     │
│                                                     │
│  Type: UpperThreshold                               │
│  Value: 70 (degrees Celsius)                        │
│  Severity: 2 (critical)                             │
│                                                     │
│  When Temperature.Actual > 70°C:                    │
│    → Charger sends NotifyEvent to CSMS              │
│    → Event includes component, variable, value,     │
│      timestamp, and severity                        │
│                                                     │
│  Monitor types:                                     │
│  ├─ UpperThreshold (value exceeds limit)            │
│  ├─ LowerThreshold (value drops below limit)        │
│  ├─ Delta (value changes by more than X)            │
│  └─ Periodic (report value every N seconds)         │
└─────────────────────────────────────────────────────┘

You set monitors via SetVariableMonitoring and receive alerts via NotifyEvent.

Deployment checklist before you rely on the device model

A device model is "ready" only when you have validated it against real hardware, not against the spec or a vendor datasheet. The six checks below are where conceptual understanding and operational reality usually part ways during a 2.0.1 rollout.

Before you call a 2.0.1 rollout "ready," validate these points against real hardware:

  1. Inventory completeness Can the charger return a usable FullInventory or ConfigurationInventory without truncation, timeout, or undocumented omissions?
  2. Write permissions Which variables are truly writable, which require reboot, and which are marked writable in theory but rejected in practice?
  3. Vendor extensions Which non-standard components and variables matter for your hardware, and how are they represented in your internal data model?
  4. Report size and performance How large are NotifyReport payloads, and can your backend ingest them without breaking observability or queueing?
  5. State normalization How do you map 2.0.1 component-variable data into the same operational views used for 1.6 chargers?
  6. Monitoring noise Which monitors are essential for operations, and which ones generate event volume without operational value?

If your team has not tested those six areas, you probably understand the device model conceptually but not operationally.

How does OCPP 2.0.1 configuration compare to OCPP 1.6?

OCPP 2.0.1 replaces 1.6's flat, string-only key-value store with a hierarchical, component-scoped model that adds typed data, real-time values, native multi-instance addressing, and built-in monitoring. The table below summarizes the differences that matter most during a rollout.

AspectOCPP 1.6OCPP 2.0.1
StructureFlat key-valueHierarchical component-variable
DiscoveryGetConfiguration (all keys)GetBaseReport (structured inventory)
ScopeStation-wide onlyComponent-scoped (station, EVSE, connector)
Data typesString onlyString, Integer, Decimal, Boolean, DateTime
Read/writeGetConfiguration / ChangeConfigurationGetVariables / SetVariables
MonitoringNone (poll only)Built-in threshold and delta monitoring
Real-time valuesNo (configuration only)Yes (Actual attribute type)
Multiple instancesNaming convention (key.1, key.2)Native EVSE/instance addressing

How EV Cloud uses the device model

The practical goal is to turn the raw component-variable tree into something operators can actually use across a mixed fleet. EV Cloud does this by discovering the model on connect, then mapping both 1.6 and 2.0.1 chargers into one consistent operational view.

  • Auto-discovery — on first connection, EV Cloud requests a full inventory and builds a live model of the charger's capabilities
  • Configuration management — view and edit all variables from the dashboard, with change tracking
  • Real-time monitoring — automatic monitors on critical variables (temperature, power, availability) with alerting
  • Fleet-wide policies — set configuration templates and apply them across charger groups
  • OCPP 1.6 compatibility — for 1.6 chargers, EV Cloud maps flat configuration keys into the device model structure for a unified interface

Next step for rollout teams

If the device model is part of an active 2.0.1 rollout, continue with:

  1. OCPP 2.0.1: what actually changed for message-level migration impact.
  2. OCPP 1.6 to 2.0.1 migration guide for coexistence strategy.
  3. How to evaluate an OCPP platform if this affects vendor selection, tooling, or rollout architecture.

Learn more about OCPP in our ultimate guide to OCPP.

Frequently asked questions

Short answers for operators evaluating this topic in production.

Continue evaluation

Turn this topic into a buying decision

Use these pages to move from protocol research into shortlist design, migration planning, and commercial evaluation.

Stay ahead of infrastructure changes

Join 2,000+ CPOs getting our insights on OCPP strategy, roaming, and scaling EV charging operations securely.

No fluff. Just hard-won infrastructure lessons. Unsubscribe anytime.

From content to rollout

Need help applying this in a live EV charging stack?

EV Cloud helps operators connect chargers, roaming partners, and internal platforms without rewriting their entire backend. Use the guide above for strategy, then use the product pages below for rollout planning.