Repository navigation
Releases: otfabric/ocppctl
Release list
v0.1.1
Date: 2026-10-19
Previous release: v0.1.0
Summary
ocppctl moves to go-ocpp v0.1.3, which fixes the three go-ocpp bugs found while building ocppctl, and is qualified against the five reference implementations of ocpp-interop v0.4.1, CitrineOS among them. Every known issue, limitation and open question, with its root cause, is in docs/KNOWN_ISSUES.md.
Fixed
- Frames lost on close (G1, go-ocpp v0.1.2). A SEND queued just before a graceful close, or the answer to a CALL from the peer (a Reset) right before the connection closed, could be lost. ocppctl's own workaround (counting written frames through the frame observer) is removed: every connection now closes with go-ocpp's graceful
Shutdown, which drains first. 20000.0refused for an integer field (G2, go-ocpp v0.1.2). EVerest libocpp's 2.0.1 NotifyEVChargingNeeds was refused byserver start; it is now accepted, as the draft-06 OCA schemas allow. The draft-04 OCPP 1.6 core schemas still refuse it, as draft-04 requires.- A SEND dropped when the peer closes right after it (G3, go-ocpp v0.1.3). SENDs received before the end of a connection are always delivered.
Changed
client run: after a Reset the simulated station is gone for 2 s before it connects again, as a charger that restarts. An immediate reconnection could be mistaken by a CSMS for the connection it had not finished closing (seen with CitrineOS).client script: when a scenario ends, a CALL from the CSMS held for a step that never ran gets the default answer and is listed as unexpected, so the connection closes gracefully at once.scripts/interop.sh: ocpp-interop v0.4.1 by default, with CitrineOS as a fifth reference (a CSMS for 1.6, 2.0.1 and 2.1). It waits until a reference CSMS has handled a close before it connects again, and until the simulated station has booted again after a Reset. A message the reference never sent (no API for it, its library cannot encode it, it refused the payload), as ocppctl's event log shows, is a skip with the reference's reason, not a failure.
Quality
- Interoperability: against ocpp-interop v0.4.1 (results in
reports/interop-v0.4.1.tsv, run with go-ocpp v0.1.2; v0.1.3 changes only the delivery of a SEND received right before a close), every fixture sample in both directions: ocppctl as the station against the CSMSs of mobilityhouse (1.6, 2.0.1, 2.1), ChargeTime (1.6, 2.0.1), lorenzodonini (1.6, 2.0.1) and CitrineOS (1.6, 2.0.1, 2.1), and the stations of mobilityhouse, ChargeTime, lorenzodonini and EVerest libocpp (1.6, 2.0.1, 2.1) against ocppctl's server. Of 1218 exchanges, 1154 passed, 29 were answered with a CALLERROR (9 of them ocppctl's own NotSupported for the PKI and VAT services it does not have), 32 were skipped because the reference did not send the message, and 3 failed: ocpp-go's schema-invalid 2.0.1 responses, which ocppctl refuses. In v0.1.0 (ocpp-interop v0.3.1, four references): 999 passed, 11 CALLERROR, 8 failed. - Tests: whole-module statement coverage 89.3% (floor 85%). New tests for the
--debugframe trace, a scenario ending with a held CALL, and a station stopped while it restarts;make stresspasses.
v0.1.0
Date: 2026-10-18
Previous release: none
Summary
First release. ocppctl is a command-line tool for OCPP 1.6J, 2.0.1 and 2.1 in both roles: a Charging Station that probes a CSMS, sends any message, runs simulated stations from a model and runs scenarios with expectations, and a CSMS that accepts stations, answers them from a model, streams what they send and sends them CALLs. Offline commands validate payloads, decode and encode frames and compute certificate hashes. Built on go-ocpp v0.1.1; every message is validated against the OCA schemas in both directions.
Highlights
- Connecting:
--url ws[s]://host[:port]/path/<identity>,--ocpp-version(the versions to offer, default2.1,2.0.1,1.6; the CSMS picks one), security profiles 0 to 3 with--insecure-transport,--password-fileorOCPPCTL_PASSWORD(never a flag value or the URL),--tls-ca,--tls-cert,--tls-key,--tls-server-name;--compression,--header,--timeout,--connect-timeout. TLS 1.2 minimum, and verification cannot be turned off. client probe: Connects and reports the negotiated version, TLS version, cipher suite and CSMS certificate, the authentication, compression and handshake time, without sending a message.--each-versiontries each version on its own. A refused handshake reports its HTTP status and meaning (401, 403, 404, 409, 400, 503).client call <Action>: One CALL with--payload(JSON,@fileor-), validated against the schema of the negotiated version before it is sent (before connecting with one--ocpp-version); the response is validated too.--rawsends an unvalidated payload.--bootsends a BootNotification first and requires Accepted (--boot-vendor,--boot-model). A state-changing CALL without a response exits 5 withoutcome_unknownand is never resent. CALLs the CSMS sends meanwhile are answered with a schema-valid default and listed.client send <Action>: One OCPP 2.1 SEND.- Shortcuts:
client boot,heartbeat,authorize,status,meter,data-transfer,transaction startandtransaction stopbuild the payload for the version the CSMS picks (StartTransaction/StopTransaction in 1.6, TransactionEvent Started/Ended in 2.x).--payload-onlyprints the payload for one version, to pipe intoclient call. client run: A simulated station from a YAML or JSON model (--config, or the built-in one): BootNotification until Accepted, honouring Pending and Rejected; StatusNotifications; heartbeats at the CSMS's interval; charging sessions with meter values; built-in behaviours for remote start and stop, Reset, TriggerMessage, GetConfiguration/ChangeConfiguration, GetVariables/SetVariables/GetBaseReport, SetVariableMonitoring/ClearVariableMonitoring, GetLocalListVersion, GetTransactionStatus, CustomerInformation, ClearTariffs and UnpublishFirmware; per-action answers from the model. Reconnects with the OCPP back-off unless--no-reconnect;--ping-interval,--duration. A{N..M}identity range runs many stations,--staggerapart, with a summary of calls, latency and errors; exit 7 when some failed.client script <file>: Scenarios ofcall,send,wait_for,expect_no_call,sleep,disconnectandreconnectsteps; expectations by path with equality,regex,presentandone_of;expect_error;saveand variables (${now},${uuid},${identity},--var); payload files; CALLs from the CSMS that arrive early held for their step, a CALL out of order with await_forfailing it at once instead of at its timeout, others answered and listed as unexpected.--keep-going; exit 0 when every step passed, 7 when some failed.client generate-config: Writes the commented station model, as YAML or JSON.server start: A CSMS for all three versions (--ocpp-version), on--listenand--path: security profiles 0 to 3 with--no-auth, passwords from the model or--passwords-file,--tls-cert/--tls-keyand--tls-client-ca(identity = certificate CN); a model (--config) for the boot status and interval per station, the token list, the 1.6 transaction ids and per-action, per-version, per-station answers;--allow,--max-stations,--ping-interval,--rate-limit,--rate-burst,--duration. Every connection, refusal, message and answer is an event on stdout.server call <Action>andserver stations: Send a CALL to a connected station, or list the stations, through the server's control socket (--control-socket,ocppctl.sockin the working directory by default, mode 0600). A CALL that may change the station needs--yes.server generate-config: Writes the commented CSMS model, as YAML or JSON.actions,validate,frame decode|encode,cert hash: The actions of a version with their sender and kind; schema validation of a request or response (exit 3 when invalid, with the field, the reason and the CALLERROR code); the OCPP-J frame codec; the CertificateHashData of a certificate, with--issuerand--hash sha256|sha384|sha512.- Output:
--format text|json|tableon every command; streams write JSON Lines. A single JSON error object on failure with--format json, with anocppobject for CALLERRORs, schema violations and refused handshakes, and stable exit codes (0, 2, 3, 4, 5, 6, 7, 10). The JSON shapes and exit codes are documented indocs/json-output.mdanddocs/exitcodes.md; the station model, scenarios and the CSMS indocs/station-model.md,docs/scenarios.mdanddocs/server-config.md. - Environment: Most flags have an
OCPPCTL_*variable; a flag wins over the environment.OCPPCTL_PASSWORDholds the password itself and is read when the command runs. --debug: Traces every frame sent and received and go-ocpp's log, and shows the causes of an error. Handshake headers and credentials are not traced.- Completion: bash, zsh, fish and PowerShell; flags with a fixed set of values complete to those values.
Limitations
Every limitation, known issue and open question of this release, with its status, is in docs/KNOWN_ISSUES.md. In short:
- No
examplecommand. go-ocpp v0.1.1 exports no generator of example payloads;--payload-onlyof the shortcuts and the payloads inexamples/are the starting points. serversends no SENDs (OCPP 2.1) to stations:server callsends CALLs only.- The 1.6 transaction ids of
serverrestart fromtransactions.first_idevery time the server starts, and are shared by all stations. Nothing the server learns is persisted. - The CSMS answers Get15118EVCertificate, GetCertificateChainStatus and VatNumberValidation with the CALLERROR
NotSupported: it has no PKI and no VAT service. A model can answer them with a fixed response. - The simulated station's built-in behaviours are the listed set (see
docs/station-model.md). Everything else is answered with the first schema-valid answer ({}or status Accepted) and has no effect: a SetChargingProfile is not stored and GetCompositeSchedule is not computed from it, a ChangeAvailability does not change a connector, an UpdateFirmware or GetLog starts no transfer and no status notifications. A 1.6 Reset and a 2.x Immediate Reset are carried out at once; a 2.x OnIdle Reset waits for the last session. The station does not Authorize before a session, and the device model and configuration values do not change its behaviour, except the heartbeat interval. - No OCPP 1.6 Security Whitepaper extensions are simulated beyond what the generic call and answers cover: ExtendedTriggerMessage, SignCertificate/CertificateSigned, InstallCertificate, GetInstalledCertificateIds, DeleteCertificate, SignedUpdateFirmware and GetLog can be sent with
client callandserver call, and are answered by defaults or the model, not by a certificate store or a log. - One connection per one-shot command.
client calland the shortcuts connect, send and disconnect; a CSMS that requires an accepted boot on the same connection needs--boot. - Simulated stations of a range share one model and one password.
Known issues
- go-ocpp v0.1.1 refuses a JSON number with a fraction part such as
20000.0for an integer field (TypeConstraintViolation), where the OCA schemas accept it. A NotifyEVChargingNeeds from EVerest libocpp 2.0.1 is refused for this reason. To be fixed in go-ocpp.
Quality
- Tests: Whole-module statement coverage above the 85% floor enforced by
make coverage(COVERAGE_MIN). Every command is tested through the real command tree, in process, against go-ocpp'socpptestCSMS and stations (internal/testcsms) and against ocppctl's own server, in every version, over ws:// and TLS, in all three output formats, with its exit codes. - Interoperability: Run against ocpp-interop v0.3.1 with
scripts/interop.sh(results inreports/interop-v0.3.1.tsv), with every fixture sample in both directions: ocppctl as the station against the CSMSs of mobilityhouse (1.6, 2.0.1, 2.1), ChargeTime (1.6, 2.0.1) and lorenzodonini (1.6, 2.0.1), and the stations of mobilityhouse, ChargeTime, lorenzodonini and EVerest libocpp (1.6, 2.0.1, 2.1) against ocppctl's server. 999 exchanges passed, 11 were answered with a CALLERROR and 8 failed, all explained:- ocppctl's CSMS answers Get15118EVCertificate, GetCertificateChainStatus and VatNumberValidation with
NotSupported(see Limitations); - NotifyEVChargingNeeds from EVerest 2.0.1 is refused with TypeConstraintViolation (see Known issues);
- upstream: the lorenzodon...
- ocppctl's CSMS answers Get15118EVCertificate, GetCertificateChainStatus and VatNumberValidation with