Onboard a Charging Fleet
This guide walks through onboarding a fleet of charging stations to Pipelet — from the moment the hardware ships to the first invoice. We’ll use the Headless CPMS API and the Python SDK for the heavy lifting.
The full source for the workflow is in cpms-headless/examples/08_full_workflow.py — this guide narrates and contextualizes it.
What you’ll automate
Section titled “What you’ll automate”- Verifying gateway health
- Discovering newly-connected stations
- Reading current OCPP configuration
- Starting a charging session
- Monitoring the session in flight
- Stopping the session and capturing the meter delta
Prerequisites
Section titled “Prerequisites”- A running Pipelet stack
- Stations physically connected and reachable to your gateway over OCPP WebSocket
- A Headless CPMS API key
export CPMS_API_KEY="hcpms_live_..."export CPMS_URL="http://localhost:8080"-
Verify the gateway. A 30-second health check before doing anything else.
import os, requests, jsonAPI_KEY = os.environ["CPMS_API_KEY"]BASE_URL = os.environ["CPMS_URL"]headers = {"X-API-Key": API_KEY, "Content-Type": "application/json"}def api_get(path, params=None):r = requests.get(f"{BASE_URL}{path}", headers=headers, params=params)return r.status_code, r.json()def api_post(path, body=None):r = requests.post(f"{BASE_URL}{path}", headers=headers, json=body or {})return r.status_code, r.json()status, data = api_get("/api/v1/system/health")assert status == 200, f"gateway unhealthy: {data}" -
Discover connected stations. As stations boot, they appear here.
status, data = api_get("/api/v1/stations/compact")for s in data:print(f" {s['station_id']:20} {s.get('vendor', '?'):20} {s.get('connector_count', 0)} connectors")compactreturns a lighter response than/stations— perfect for monitoring loops. -
Read OCPP configuration. Some keys you almost always want to verify on a fresh station:
HeartbeatInterval,MeterValueSampleInterval,LocalAuthListEnabled.STATION = "WALLBOX_001"status, data = api_post("/api/v1/configuration/get",{"station_id": STATION, "key": ["HeartbeatInterval", "MeterValueSampleInterval"]},)for kv in data.get("configurationKey", []):print(f" {kv['key']} = {kv['value']}")If the values are wrong, write them with
/api/v1/configuration/set— see the API reference for the body schema. -
Start a charging session. Use a known-good idTag from your customer database.
status, data = api_post("/api/v1/transactions/start",{"station_id": STATION, "connectorId": 1, "idTag": "RFID_DEMO"},)if data.get("status") != "Accepted":raise RuntimeError(f"Remote start rejected: {data}")transaction_id = data["transactionId"] -
Monitor the session. In production, you’d subscribe to webhooks. For a quick interactive check, poll
/api/v1/transactions:import timefor _ in range(3):time.sleep(5)_, txns = api_get("/api/v1/transactions")for tx in txns.get("transactions", []):print(f" Station={tx['chargepoint_id']} Energy={tx.get('energy_wh', 0)} Wh") -
Stop the session.
status, data = api_post("/api/v1/transactions/stop-connector",{"station_id": STATION, "connectorId": 1},)
With the Python SDK
Section titled “With the Python SDK”The same workflow, but cleaner with wallecpms:
from wallecpms import HeadlessCPMS
with HeadlessCPMS(BASE_URL, api_key=API_KEY) as cpms: assert cpms.system.health()["status"] == "ok"
for station in cpms.stations.list_compact(): print(station["station_id"])
cpms.configuration.get(station_id="WALLBOX_001", keys=["HeartbeatInterval"])
result = cpms.transactions.start("WALLBOX_001", connector_id=1, id_tag="RFID_DEMO") txn_id = result["transactionId"]
# ...later cpms.transactions.stop_connector("WALLBOX_001", connector_id=1)Productionizing the flow
Section titled “Productionizing the flow”A real onboarding flow does more than this script:
- Webhooks instead of polling. Subscribe to
StartTransaction,StopTransaction, andStatusNotificationso your billing system updates without polling. See Webhooks Overview. - Bulk operations. When onboarding 50+ stations at once, fan out the configuration writes in parallel. The gateway is concurrency-safe.
- Pre-flight validation. Before accepting a station into production, verify firmware version, supported OCPP version, and a successful
BootNotificationround-trip. - Failure modes. Stations can be physically connected but mis-configured — handle
Rejectedresponses andFaultedconnector status gracefully. - Audit trail. Log every API call with the operator who triggered it. Pipelet’s gateway logs include the API key name, but you’ll want app-level audit too.
- Webhooks Overview — replace the polling loop with push.
- API Reference: Stations — every station endpoint with parameters and responses.
- Self-Hosting — when you’re ready to run Pipelet on your own infrastructure.