Skip to main content

Overview

An EV charger’s maximum charging power is a ceiling the charger operates under, not a one-off command, so it is a device setting: max_charge_rate in kw, or max_charge_current in amps. Write it once and it persists across sessions until you change it or the charger reverts to its own defaults. Use it to balance against household consumption, throttle to a cheaper tariff window, or coordinate multiple chargers behind a single supply. Commands can carry a rate cap too, and the difference is what makes each one the right tool. A power or current parameter on a charge command caps that session and nothing else. max_charge_rate is the standing ceiling the charger stays under whatever anyone asks it for next — including a session someone starts from the manufacturer’s own app. Reach for the setting when the constraint belongs to the site, and for the command parameter when it belongs to the session. Commands run through POST /ev-charger/{deviceId}; the setting runs through POST /ev-charger/{deviceId}/settings. See canonical actions on the actions-versus-settings boundary. Pick one unit, not both. Kilowatts and amperes describe the same ceiling, so a request carrying both leaves it ambiguous and is refused rather than resolved. On a command that is 422 UNSUPPORTED_PARAMETER_COMBINATION (details.conflictingParameters); on the settings write it is 422 UNSUPPORTED_SETTING_COMBINATION (details.conflictingSettings). A charger declaring both is normal and means you may write in either one.
Coming soon. Live control for EV chargers. The sandbox environment serves the full commands and settings surface, so the walkthrough below works against a sandbox device today. A live EV charger push returns 503 NOT_YET_AVAILABLE until the live path opens.

Step 1: Read the current state

Every response is wrapped in { success, data, meta }; the device sits under data.
The charger is connected and drawing 11 kW. data.settings.max_charge_rate is the writable ceiling, bounded 0 to 50 kw in steps of 0.1 — a value between the increments is refused, so read step before you write. commands is abridged here; a charger that implements the full surface also advertises the two surplus modes, auto.charge_surplus_only (pauses when generation drops) and auto.charge_surplus_first (lets the grid top up). Every auto.* mode takes an empty parameters map and no windowed execution: the mode hands timing to the charger’s own optimiser and runs until another command replaces it. metadata.source reports where the reading came from. Sandbox device reads always carry source: "projection" because sandbox devices are simulated, not physical hardware. In live, the value is one of cache, live, or fallback, naming the data-freshness tier the read came back from.

Step 2: Cap the power

Write max_charge_rate through the settings endpoint. The body is a sparse map: send only the settings you want to change. The value is the canonical {value, unit} shape.
Settings are fire-and-forget. The response acknowledges the write and echoes the keys that changed, under the standard data envelope.
A value outside 0 to 50, or one that misses the 0.1 kw grid, returns 422 SETTING_OUT_OF_RANGE with min, max, and step in details so you can round and retry without a second GET. An unknown key returns 422 UNSUPPORTED_SETTING; a read-only key returns 422 READ_ONLY_SETTING. Sending max_charge_rate and max_charge_current together returns 422 UNSUPPORTED_SETTING_COMBINATION. The bounds come from the device read, so check data.settings on the GET before you write.

Step 3: Verify the cap applied

Read the device again. The new ceiling shows on data.settings, and live throughput settles to the cap.

Start and stop a session

The cap is configuration; starting and stopping a session is intent. Commands go in the canonical action envelope. Read commands on the device to see which of these a given charger declares and what each one accepts — a command that is not in the map is refused with 422 UNSUPPORTED_MODE, which lists the ones that are. Set the cap before starting a session to keep the first kWh inside a cheap rate, or change it mid-session as household demand spikes. The most recent setting wins.
This is an immediate push, so the action begins life in acknowledged rather than passing through scheduled. To pause without unplugging, push idle. Only one command is in flight at a time; submit a second while the first is acknowledged and you get 409 CONFLICT. Add onConflict: "cancel_and_replace" to drop the in-flight action and run the new one, or queue_after to hold yours until the running window closes. queue_after needs an end to queue behind. An auto.* command has none, because it runs until another command replaces it, so queueing behind one returns 409 CONFLICT and points at cancel_and_replace instead. A conflicting action still mid-flight returns 409 CONFLICT_IN_EXECUTION with an empty strategy list, which means wait for it.

Why this works

The split between the max_charge_rate setting and the charger’s commands is the actions-versus-settings boundary applied to a charger. A ceiling is persistent and non-conflicting, so it is a setting; starting a session or handing timing to a strategy is a time-bound intent, so it is a command. The same boundary puts a battery’s safety_reserve on settings and its charge on commands. See canonical actions and capabilities.

What next

Subscribe to webhooks

Get push.completed events on charger state changes.

Handle conflicts

Resolve 409s when a charger write is already in flight.

Hold an HVAC device

The mirrored command pattern on a thermostat.

Canonical actions

Where EV charger commands sit in the canonical model.