What this changes for deployers
If you are implementing OCPP 2.0.1 in production, the device model is one of the first places where "spec support" and "deployment readiness" diverge.
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 OCPP 1.6's configuration model broke down
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, WiFi, 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.
The component-variable architecture
The device model is a tree of components, each with variables that describe its state and configuration.
┌──────────────────────────────────────────────────────────────┐
│ 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 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 → 60Standard components
OCPP 2.0.1 defines a set of standard components that all chargers should support:
| Component | Description | |-----------|------------| | ChargingStation | The station itself | | EVSE | Each charging point | | Connector | Physical connector | | Controller | OCPP communication controller | | SecurityCtrlr | Security settings | | AuthCtrlr | Authorization settings | | TxCtrlr | Transaction settings | | SampledDataCtrlr | Meter value configuration | | MonitoringCtrlr | Monitoring and alerting | | ClockCtrlr | Time synchronization | | DisplayMessageCtrlr | On-screen messages | | LocalAuthListCtrlr | Local authorization list | | SmartChargingCtrlr | Smart charging capabilities | | ReservationCtrlr | Reservation settings |
Vendors can add custom components for hardware-specific features.
Variables and attribute types
Each variable has up to four attribute types, each representing a different aspect:
┌─────────────────────────────────────────────────────┐
│ 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)
Reading the device model
OCPP 2.0.1 provides two messages for reading the device model:
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.
Writing to the device model
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.
Monitoring
The device model also supports monitoring — the charger can alert the backend when variables change or cross thresholds.
┌─────────────────────────────────────────────────────┐
│ 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
Before you call a 2.0.1 rollout "ready," validate these points against real hardware:
- Inventory completeness
Can the charger return a usable
FullInventoryorConfigurationInventorywithout truncation, timeout, or undocumented omissions? - Write permissions Which variables are truly writable, which require reboot, and which are marked writable in theory but rejected in practice?
- Vendor extensions Which non-standard components and variables matter for your hardware, and how are they represented in your internal data model?
- Report size and performance
How large are
NotifyReportpayloads, and can your backend ingest them without breaking observability or queueing? - State normalization How do you map 2.0.1 component-variable data into the same operational views used for 1.6 chargers?
- 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.
Comparison: OCPP 1.6 vs 2.0.1 configuration
| Aspect | OCPP 1.6 | OCPP 2.0.1 | |--------|----------|------------| | Structure | Flat key-value | Hierarchical component-variable | | Discovery | GetConfiguration (all keys) | GetBaseReport (structured inventory) | | Scope | Station-wide only | Component-scoped (station, EVSE, connector) | | Data types | String only | String, Integer, Decimal, Boolean, DateTime | | Read/write | GetConfiguration / ChangeConfiguration | GetVariables / SetVariables | | Monitoring | None (poll only) | Built-in threshold and delta monitoring | | Real-time values | No (configuration only) | Yes (Actual attribute type) | | Multiple instances | Naming convention (key.1, key.2) | Native EVSE/instance addressing |
How EV Cloud uses the device model
EV Cloud fully leverages the OCPP 2.0.1 device model:
- 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:
- OCPP 2.0.1: what actually changed for message-level migration impact.
- OCPP 1.6 to 2.0.1 migration guide for coexistence strategy.
- How to evaluate an OCPP platform if this affects vendor selection, tooling, or rollout architecture.