Skip to main content

PyOCMF

PyPI version Python versions CI ty

Python library for parsing, validating, and verifying OCMF (Open Charge Metering Format) signatures from electric vehicle charging stations.

Note: This is an unofficial library that implements parts of the OCMF specification. It is not affiliated with or endorsed by S.A.F.E. e.V.. For official verification of charging session data, please use the Transparenzsoftware provided by S.A.F.E. e.V. This library may be incomplete or contain discrepancies from the official specification.

Try PyOCMF in your browser - No installation required! Paste a record, open an XML file or scan a QR code, and check it locally in your web browser using Pyodide.

Features

  • Parse OCMF strings into validated Python objects
  • Verify cryptographic signatures for data integrity
  • Support for ECDSA with multiple curves (secp192k1, secp256k1, secp192r1, secp256r1, secp384r1, secp521r1, brainpool variants)
  • Type-safe models using Pydantic
  • Accepts every record the Transparenzsoftware accepts, reports spec deviations as warnings, and offers an optional strict mode
  • Eichrecht compliance checks aligned with the Transparenzsoftware
  • Command-line interface for verification, compliance checks and inspection

Installation

pip install pyocmf[all]

This installs the complete package with CLI tools and cryptographic signature verification.

Alternative installation options
# Minimal (parsing only, no CLI or crypto)
pip install pyocmf

# With CLI only
pip install pyocmf[cli]

# With crypto only
pip install pyocmf[crypto]

Quick Start

from pyocmf import OCMF

# Parse an OCMF string
ocmf_string = 'OCMF|{"FV":"1.0","GI":"KEBA_KCP30",...}|{"SD":"3045..."}'
ocmf = OCMF.from_string(ocmf_string)

# Access payload data
print(ocmf.payload.GI)  # Gateway ID: "KEBA_KCP30"
print(ocmf.payload.GS)  # Gateway serial number
print(ocmf.payload.RD)  # List of meter readings

# Verify signature with a hex or base64 public key (requires pyocmf[crypto])
is_valid = ocmf.verify_signature(public_key)

# Strings with the key appended (OCMF|payload|signature|key), as in QR codes for the
# Transparenzsoftware, verify without one; parsing them emits a SpecWarning
is_valid = OCMF.from_string(ocmf_with_key).verify_signature()

Command Line Interface

# Verify the signature and check Eichrecht compliance
ocmf 'OCMF|{"FV":"1.0",...}|{"SD":"3045..."}' --public-key 3059301306072A8648CE3D...

# XML files carry their public keys; their transactions are checked as a whole
ocmf charging_session.xml

# Check a begin and an end record as one transaction
ocmf check begin.xml end.xml

# Show the parsed record
ocmf inspect 'OCMF|{...}|{...}'

More Examples

Working with XML files

OCMF data is often distributed in XML format from CPO backends.

from pyocmf import OcmfContainer

container = OcmfContainer.from_xml("charging_session.xml")

for entry in container:
    print(f"Gateway: {entry.ocmf.payload.GI}")
    if entry.public_key:
        is_valid = entry.verify_signature()
        print(f"Signature: {'Valid' if is_valid else 'Invalid'}")

# Records sharing a transactionId are checked as one transaction, as the
# Transparenzsoftware does; other records are checked on their own
for result in container.check_eichrecht(errors_only=True):
    print(result.transaction_id, "compliant" if result.is_compliant else result.issues)
Eichrecht compliance checking

Validate German calibration law requirements for charging transactions.

from pyocmf import OCMF

ocmf_begin = OCMF.from_string(begin_string)
ocmf_end = OCMF.from_string(end_string)

issues = ocmf_begin.check_eichrecht(ocmf_end, errors_only=True)
if not issues:
    print("Transaction is Eichrecht compliant")

# A single record holding both the begin and the end reading is checked the same way
issues = OCMF.from_string(record_string).check_eichrecht(errors_only=True)

The checks follow the Transparenzsoftware. They compare the begin and end readings on law-relevant registers (loss-compensated registers take precedence) and report errors for a meter status other than OK, an energy error flag, a missing or repeated begin or end reading, decreasing values or timestamps, mismatching OBIS codes, units, serial numbers, event counters (format versions before 0.5) or pagination contexts, and invalid identification levels. Time synchronization, time error flags, cable loss, identification data mismatches and pagination gaps are reported as warnings.

Public key metadata

Extract structured metadata from public keys per OCMF spec Table 23.

from pyocmf import PublicKey

public_key = PublicKey.from_string(public_key_hex)
print(f"Curve: {public_key.curve}")
print(f"Key Size: {public_key.size} bits")

# Check if key matches signature algorithm
matches = public_key.matches_signature_algorithm(ocmf.signature.SA)

Supported Signature Algorithms

PyOCMF supports the ECDSA signature algorithms defined in the OCMF specification:

  • secp192k1, secp256k1 - Koblitz curves
  • secp192r1, secp256r1, secp384r1, secp521r1 - NIST curves
  • brainpool256r1, brainpoolP256r1, brainpool384r1 - Brainpool curves
  • SHA256 and SHA512 hash functions

The cryptography package does not support secp192k1, so pyocmf verifies it with the pure-Python ecdsa package, which pyocmf[crypto] also installs.

Error Handling

from pyocmf import OCMF, SignatureVerificationError, OcmfFormatError

try:
    ocmf = OCMF.from_string(ocmf_string)
    is_valid = ocmf.verify_signature(public_key)
except OcmfFormatError as e:
    print(f"Invalid OCMF format: {e}")
except SignatureVerificationError as e:
    print(f"Signature verification error: {e}")

All library errors derive from PyOCMFError.

Spec Deviations

pyocmf accepts every record the Transparenzsoftware accepts. Values outside the OCMF specification, such as unknown codes, exceeded lengths or missing mandatory fields, are kept as-is and reported as a SpecWarning instead of failing to parse:

import warnings

from pyocmf import OCMF, OcmfContainer, SpecWarning

with warnings.catch_warnings(record=True) as caught:
    warnings.simplefilter("always", SpecWarning)
    ocmf = OCMF.from_string(ocmf_string)

for warning in caught:
    print(warning.message)

Pass strict=True to reject non-compliant records instead. The deviation is raised as the usual OcmfFormatError, OcmfPayloadError or OcmfSignatureError:

ocmf = OCMF.from_string(ocmf_string, strict=True)
container = OcmfContainer.from_xml("transaction.xml", strict=True)

On the command line, use --strict.

Development

# Clone the repository with its submodules (OCMF spec and Transparenzsoftware test data)
git clone --recursive https://github.com/paul-ww/pyocmf.git
cd pyocmf

# Install dependencies with uv
uv sync

# Run tests
uv run pytest

# Run type checking
uv run ty check src test

# Run linting
uv run ruff check .

See AGENTS.md for project conventions, including Transparenzsoftware parity.

Documentation

Full documentation, including the CLI and API reference, is available at paul-ww.github.io/pyocmf.

License

MIT, see LICENSE.

About OCMF

OCMF (Open Charge Metering Format) is a standardized format for metering data from electric vehicle charging stations. It ensures transparency and tamper-proof documentation of charging sessions, complying with legal requirements such as the EU Measuring Instruments Directive (MID) and German Eichrecht.

For more information about OCMF, visit safe-ev.de.


Vibe-engineered with Claude Code 🤖

Metadata

Release files for pyocmf 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyocmf 0.6.0
File Size Uploaded
pyocmf-0.6.0.tar.gz 36.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyocmf 0.6.0
File Interpreter ABI Platform
pyocmf-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 87.5 kB

Release files / pyocmf-0.6.0.tar.gz

Download URL pyocmf-0.6.0.tar.gz
Size 36.2 kB
Tags Source
SHA-256 checksum
How to use checksums
1911ff3c07179e92d58d662fc215861c0901ae45fe31f1d1eb6adf2ce0a5ba2f
BLAKE2b-256 checksum
How to use checksums
b039b3d7af485c92bb2377bc876de5b63e17a905523a9a9f4ec9ec0db04e92a2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / pyocmf-0.6.0-py3-none-any.whl

Download URL pyocmf-0.6.0-py3-none-any.whl
Size 51.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a5a1f244c0639a527c9b6f8c4b0eab472ec1992738f7bdf57b653d8922293f90
BLAKE2b-256 checksum
How to use checksums
8c01da5b6fc9f2de97937d3bc57e68ced878b69bbd35b7d27cb4ba85a872073c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page