This application aggregates chargepoint data from different sources and provices it without authentication using an OCPI-style JSON-API and a vector tile server.
| name | uid | realtime | credentials | comment |
|---|---|---|---|---|
| Bundesnetzagentur: API | bnetza_api | false | false | Additional config ignore_operators:: list[str] is supported, which will ignore given operators during import. Set to weekly download, as it does not change so often. |
| Bundesnetzagentur: Excel | bnetza_excel | false | false | |
| chargecloud: Stadtwerke Pforzheim | chargecloud_pforzheim | true | false | |
| chargecloud: Stadtwerke Stuttgart | chargecloud_stuttgart | true | false | |
| chargecloud: Stadtwerke Tübingen | chargecloud_tuebingen | true | false | |
| chargecloud: Stadtwerke Ludwigsburg | chargecloud_ludwigsburg | true | true | |
| Datex2: Ampeco | datex2_ampeco | true | true | Via Mobilithek, currently disfunctional |
| Datex2: Chargecloud | datex2_chargecloud | true | true | Via Mobilithek |
| Datex2: EcoMovement | datex2_ecomovement | true | true | Via Mobilithek |
| Datex2: Elu Mobility | datex2_elu_mobility | true | true | Via Mobilithek, currently disfunctional |
| Datex2: EnBW | datex2_enbw | true | true | Via Mobilithek |
| Datex2: enio | datex2_enio | true | true | Via Mobilithek |
| Datex2: Stadt Erft | datex2_erft | true | true | Via Mobilithek, currently disfunctional |
| Datex2: eRound | datex2_eround | true | true | Via Mobilithek |
| Datex2: Grid&Co | datex2_gridandco | true | true | Via Mobilithek |
| Datex2: ladebusiness | datex2_ladebusiness | true | true | Via Mobilithek, currently disfunctional |
| Datex2: Lichtblick | datex2_lichtblick | true | true | Via Mobilithek |
| Datex2: Midorion | datex2_midorion | true | true | Via Mobilithek |
| Datex2: Monta | datex2_monta | true | true | Via Mobilithek, currently disfunctional |
| Datex2: msu | datex2_msu | true | true | Via Mobilithek |
| Datex2: Pump | datex2_pump | false | true | Via Mobilithek, currently disfunctional |
| Datex2: Qwello | datex2_qwello | true | true | Via Mobilithek |
| Datex2: Smatrics | datex2_smatrics | true | true | Via Mobilithek, currently particially disfunctional |
| Datex2: Taubert Consulting | datex2_taubert | true | true | Via Mobilithek |
| Datex2: Tesla | datex2_tesla | true | true | Via Mobilithek |
| Datex2: Vaylens | datex2_vaylens | true | true | Via Mobilithek, currently disfunctional |
| Datex2: Volkswagen Charging Group | datex2_volkswagen | true | true | Via Mobilithek |
| Datex2: Wirelane | datex2_wirelane | true | true | Via Mobilithek, currently disfunctional |
| PBW | eaaze_pbw | true | true | |
| Giro-e | giroe | true | true | |
| Heilbronn Heckarbogen | heilbronn_neckarbogen | true | true | |
| Lichtblick | lichtblick | true | true | Currently dysfunctional |
| OCHP: Albwerk | ochp_albwerk | true | true | |
| OCHP: Ladenetz | ochp_ladenetz | true | true | |
| OCPI: Stadtnavi | ocpi_stadtnavi | true | false | |
| OpenData Swiss | opendata_swiss | true | false |
There is a matching algorithm which matches live data sources with bnetza sources. You can find details at our matching docs.
At api.ocpdb.de you will find an OpenAPI documentation of public endpoints you can use.
The application provides a simple command line interface. You can access any cli command from within the container. The makefile provides a shortcut to run the cli:
make docker-run CMD="flask db upgrade"flask import allflask import static example_sourceflask import realtime example_sourceflask import images example_sourceflask source listflask source delete example_sourceflask match runflask location push-mobilithek-static # Full static DATEX II snapshot
flask location push-mobilithek-realtime-full # Full realtime status snapshot
flask location push-mobilithek-realtime-diff # Incremental realtime status diffSee DATEX II push to Mobilithek for details.
In addition to importing DATEX II data via Mobilithek (see the datex2_* data sources above), OCPDB can also
publish its aggregated dataset back to the Mobilithek in DATEX II format. Three kinds of
push are supported:
- Static snapshot – the full set of locations, charging stations, EVSEs, connectors, tariffs and operators.
- Realtime full snapshot – the current EVSE status of all locations (a
SNAPSHOT_PUSH). - Realtime diff – an incremental
DELTA_PUSHcontaining only EVSEs whose status changed since the last push. The last successful push timestamp is stored in Redis (last_datex_realtime_push); if nothing changed, no message is sent but the watermark is still advanced.
Both DATEX II 3.5 and 3.7 are supported, selected via MOBILITHEK_VERSION.
The pushes can be triggered manually through the CLI:
flask location push-mobilithek-static # Full static DATEX II snapshot
flask location push-mobilithek-realtime-full # Full realtime status snapshot (SNAPSHOT_PUSH)
flask location push-mobilithek-realtime-diff # Incremental realtime status diff (DELTA_PUSH, skipped when empty)When MOBILITHEK_ENABLED is true, the pushes are scheduled automatically as Celery periodic tasks. The schedule is
configurable in config.yaml:
MOBILITHEK_ENABLED: true
MOBILITHEK_VERSION: '3.5' # DATEX II version, '3.5' or '3.7'
MOBILITHEK_NAME: binary butterfly GmbH # supplier name sent in the exchange context
MOBILITHEK_STATIC_PUBLICATION_ID: 980054862909370368
MOBILITHEK_REALTIME_PUBLICATION_ID: 980084361378099200
MOBILITHEK_CERTIFICATE_FILENAME: 'mobilithek-binary-butterfly.crt.pem' # client certificate in KEY_DIR
MOBILITHEK_KEY_FILENAME: 'mobilithek-binary-butterfly.key.pem' # client key in KEY_DIR
# Push schedule (only active when MOBILITHEK_ENABLED is true):
MOBILITHEK_STATIC_PUSH_HOUR: 3 # static snapshot once a day at this hour ...
MOBILITHEK_STATIC_PUSH_MINUTE: 0 # ... and minute
MOBILITHEK_REALTIME_FULL_PUSH_FREQUENCY: 21600 # full realtime snapshot, in seconds (default every 6 hours)
MOBILITHEK_REALTIME_DIFF_PUSH_FREQUENCY: 60 # incremental realtime diff, in seconds (default every minute)Messages are sent via mutual-TLS POST to the Mobilithek publication endpoint, using the client certificate and key
located in KEY_DIR.
OCPDB extends the Location data model with a new field official_regional_code in non-strict mode. This field provides
the official regional code of a location, if available. Following regional codes are used:
- DEU: Regionalschlüssel
In Germany, we use the dataset Verwaltungsgebiete 1:25 000 (VG25) by Bundesamt für Kartographie und Geodäsie (BKG) for assigning official regional codes to locations, licenced as Creative Commons Namensnennung 4.0 International. We download the data using wget, transform the data using ogr2ogr and store it our Postgis database.
The script assumes that a once-only import is sufficient. You must delete the data/regionalschluessel/.vg25-imported "marker file" (and re-run the script) to trigger a re-import.
It also assumes that the data at the VG25 URL is immutable, the data will be downloaded only once. You must delete data/regionalschluessel/vg25.gpkg (and re-run the script) to trigger a re-download.
data/regionalschluessel/vg25.gpkg in order to trigger the download again, and data/regionalschluessel/.vg25-imported
to trigger the import again. You can use this mechanism for ansible automatization, too: if you drop the geopackage
at data/regionalschluessel/vg25.gpkg via ansible, you won't need to download the file during runtime.
You can run
flask location assign-regionalschluesselto assign regional codes to all locations already in the database. You can limit it to specific locations by providing the source id:
flask location assign-regionalschluessel --source-id 1In case of an Regionalschlüssel file update, make sure that the new geopackage has the same format as the old one. Afterwards, you can run
flask location assign-regionalschluessel --re-assignto re-assign regional codes to all locations.
The installation process is documented at INSTALL.md.
This project uses uv for dependency management. All runtime and
development dependencies are declared in pyproject.toml, and exact versions are pinned in
uv.lock. There are no requirements.txt files anymore.
- Runtime dependencies live in
[project.dependencies]. - Development / test dependencies live in the
devgroup under[dependency-groups]. - The shared
flask-openapiandbutterfly_pubsublibraries are served from binary butterfly's private package index, which is configured under[tool.uv.sources]/[[tool.uv.index]].
Common commands (all run inside the Docker containers in development, see the Makefile):
# Install all dependencies (incl. dev) into a virtual environment, respecting the lockfile
uv sync
# Install only runtime dependencies (used for the production image)
uv sync --no-dev
# Add / remove a dependency (updates pyproject.toml and uv.lock)
uv add some-package
uv remove some-package
# Upgrade the lockfile to the latest allowed versions
uv lock --upgrade
# Run a command inside the environment
uv run flask import allAfter changing dependencies, rebuild the dev image with make docker-build so the containers pick
up the new packages.
The application uses the logging module with some optional extensions. Logging can be configured using the config.yml.
The application provides some additional context and / or special output formats to log entries with custom formatters:
Most requests or tasks have context which can be used in log entries. The simplest is
LOGGING:
formatters:
human_readable:
(): webapp.common.logging.formatter.flask_attributes_formatter.FlaskAttributesFormatter
format: '%(asctime)s %(levelname)s %(source)s: %(message)s'
defaults: {'source': '-'}
handlers:
my_handler:
formatter: human_readableWith this example, you add the source to every log entry (if available). Please keep in mind that you need to
add a default, because not every log entry has a source_uid context.
Following additional log context variables are available:
sourceinitiatorlocationevseimage
You can output log entries in OpenTelemetry format, too:
LOGGING:
formatters:
open_telemetry:
(): webapp.common.logging.formatter.flask_open_telemetry_formatter.FlaskOpenTelemetryFormatter
prefix: ocpdb
service_name: OCPDB
handlers:
my_handler:
formatter: open_telemetryContext is automatically injected into log entry Attributes.
OCPDB is under AGPL. You will find details at the LICENCE.txt.
We appreciate bug reports and feature requests.