139 lines
7.2 KiB
Markdown
139 lines
7.2 KiB
Markdown
# vw-eu-data-act-prometheus-exporter
|
|
|
|
A Prometheus exporter for vehicle data from the
|
|
[VW Group EU Data Act Portal](https://eu-data-act.drivesomethinggreater.com/)
|
|
(Volkswagen; not supported for Audi/Skoda/SEAT/CUPRA without adjustments).
|
|
|
|
For each configured VIN, a dedicated background thread logs into the portal,
|
|
downloads the latest available dataset, and derives Prometheus metrics from
|
|
it - following the classic multi-target-exporter pattern via a `vin` label on
|
|
a shared `/metrics` endpoint (no Docker, plain systemd deployment).
|
|
|
|
## Features
|
|
|
|
- Multiple vehicles (VINs) under one VW account, each with its own
|
|
scrape-interval timing in the background
|
|
- **No persistent state by default**: no ZIPs, no JSON, no values are
|
|
written to disk - everything lives in process memory and is empty again
|
|
after a restart. Optionally, the JSON extracted from every downloaded
|
|
dataset - including "_no_content_found.zip" placeholders, if they carry a
|
|
JSON payload - can be persisted to disk via `config.persist_raw_json`
|
|
(see [Configuration](#configuration-configpy)). ZIP files themselves are
|
|
never written to disk
|
|
- Fields are only updated when they are present in the current dataset - if
|
|
a field is missing, the last known value is kept
|
|
- The `health` metric cleanly distinguishes between "portal ok, but no new
|
|
data (yet)" (not a failure) and actual failures (login, HTTP, timeout, or
|
|
parse errors)
|
|
|
|
## Metrics
|
|
|
|
All metrics carry the `vin` label; all names carry the configurable prefix
|
|
(default: `vw_eu_data_act_exporter_`).
|
|
|
|
| Metric | Type | Description |
|
|
|---|---|---|
|
|
| `mileage_km` | Gauge | Odometer reading |
|
|
| `hvsoc_percent` | Gauge | HV battery state of charge in % |
|
|
| `driver_present` | Gauge (bool) | Driver detected in vehicle |
|
|
| `cruising_range_km` | Gauge | Remaining range in km |
|
|
| `hvbattery_temperature_max_celsius` | Gauge | Max. HV battery temperature |
|
|
| `hvbattery_temperature_min_celsius` | Gauge | Min. HV battery temperature |
|
|
| `charging_state` | Enum | `chargingStatus.currentChargeState` |
|
|
| `charge_power_kw` | Gauge | Current charging power in kW |
|
|
| `plug_connection_state` | Enum | Plug connection status |
|
|
| `target_soc_percent` | Gauge | Charge target in % |
|
|
| `position_longitude` / `position_latitude` | Gauge | Last known position (only set when **both** coordinates were present in the same dataset) |
|
|
| `position_created_timestamp_seconds` | Gauge | Unix timestamp of the last known position |
|
|
| `locked` | Gauge (bool) | Combined lock status of all doors, trunk and hood (only `unlocked` if at least one component actively reports `UNLOCKED`) |
|
|
| `is_parked` | Gauge (bool) | Vehicle parked |
|
|
| `parking_brake_engaged` | Gauge (bool) | Parking brake engaged |
|
|
| `driving_mode` | Enum | Active driving mode |
|
|
| `next_service_type` | Enum | Next due service type |
|
|
| `service_due_in_days` | Gauge | Remaining days until the next service |
|
|
| `last_vehicle_signal_timestamp_seconds` | Gauge | Timestamp of the last vehicle signal (`carCapturedUTCTimestamp`) |
|
|
| `uptime_seconds` | Gauge | Uptime of the exporter process |
|
|
| `last_successful_scrape_timestamp_seconds` | Gauge | Last scrape with **new** data |
|
|
| `health` | Gauge (bool) | `0` initially and after a failure, `1` once new data has been received at least once |
|
|
| `http_requests_total` | Counter | Total number of HTTP requests made to the portal (login + API calls) |
|
|
|
|
**Note on enum metrics:** The state space (e.g. possible values of
|
|
`drivingMode` or `chargingStatus.currentChargeState`) is compiled
|
|
best-effort from the sample dataset and comparable projects, and each one
|
|
includes an `UNKNOWN` fallback. If an unknown raw value shows up, it is
|
|
exported as `UNKNOWN` and additionally logged as `WARNING` - the state list
|
|
in `vw-eu-data-act-exporter.py` (the `*_STATES` constants) can then be
|
|
extended.
|
|
|
|
## Installation (systemd)
|
|
|
|
```bash
|
|
# 1. Create directory + user (the "prometheus" system user is typically
|
|
# already present if Prometheus itself is installed on this host)
|
|
sudo mkdir -p /opt/vw-eu-data-act-exporter
|
|
|
|
# 2. Copy files
|
|
sudo cp vw-eu-data-act-exporter.py config.py requirements.txt /opt/vw-eu-data-act-exporter/
|
|
|
|
# 3. Virtualenv + dependencies
|
|
sudo python3 -m venv /opt/vw-eu-data-act-exporter/venv
|
|
sudo /opt/vw-eu-data-act-exporter/venv/bin/pip install -r /opt/vw-eu-data-act-exporter/requirements.txt
|
|
|
|
# 4. Adjust config.py (VW account, VINs, interval)
|
|
sudo nano /opt/vw-eu-data-act-exporter/config.py
|
|
|
|
# 5. Permissions (config.py contains the VW account password in plain text)
|
|
sudo chown -R prometheus:prometheus /opt/vw-eu-data-act-exporter
|
|
sudo chmod 600 /opt/vw-eu-data-act-exporter/config.py
|
|
|
|
# 6. Install systemd unit
|
|
sudo cp vw-eu-data-act-exporter.service /etc/systemd/system/
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now vw-eu-data-act-exporter
|
|
sudo systemctl status vw-eu-data-act-exporter
|
|
```
|
|
|
|
## Configuration (`config.py`)
|
|
|
|
| Field | Meaning |
|
|
|---|---|
|
|
| `hostName` / `serverPort` | Bind address of the `/metrics` endpoint |
|
|
| `exporter_prefix` | Prefix for all metric names (only `[a-zA-Z0-9_:]`, e.g. `vw_eu_data_act_exporter_`) |
|
|
| `vw_account` | E-mail + password of the VW account under which **all** configured VINs are registered |
|
|
| `vins` | List of vehicle VINs to scrape |
|
|
| `scrape_interval_minutes` | Poll interval per vehicle. The portal only delivers new datasets for "continuous" data requests roughly every ~15 min anyway - a shorter interval will not yield fresher data, but will increase the login frequency against the account |
|
|
| `persist_raw_json` | `False` by default. When set to `True`, the JSON extracted from every downloaded dataset is written to disk (one file per VIN per scrape), including the JSON extracted from `_no_content_found.zip` placeholders when they carry one. ZIP files are never written to disk, only the extracted JSON - useful while tuning the field mapping in `vw-eu-data-act-exporter.py`. Not needed for normal operation |
|
|
| `persist_raw_json_dir` | Directory the raw JSON files are written to when `persist_raw_json` is enabled (default: `./raw_json`, relative to the working directory) |
|
|
|
|
Prerequisite in the portal (one-time, in a browser): connect vehicle ->
|
|
"Get customised data" -> enable continuous, 15-minute frequency (see the
|
|
docstring in `vw-eu-data-act-exporter.py`).
|
|
|
|
## Prometheus integration
|
|
|
|
```yaml
|
|
scrape_configs:
|
|
- job_name: vw-eu-data-act-exporter
|
|
static_configs:
|
|
- targets: ["127.0.0.1:9109"]
|
|
```
|
|
|
|
Since the exporter itself polls in the background (it does not hit the
|
|
portal live on every Prometheus scrape), the Prometheus scrape interval can
|
|
be independent of, and significantly shorter than, `scrape_interval_minutes`
|
|
- it will simply repeatedly return the same cached value.
|
|
|
|
## Troubleshooting
|
|
|
|
- `health` stays `0` permanently: check
|
|
`journalctl -u vw-eu-data-act-exporter -f` - usually a login failure
|
|
(wrong password) or no datasets available in the portal yet (see
|
|
prerequisite above).
|
|
- A single metric is missing entirely: the corresponding field has never
|
|
been present in any dataset fetched so far (e.g.
|
|
`position_longitude`/`_latitude`, if both coordinates were never
|
|
delivered at the same time).
|
|
- `WARNING ... Unknown enum value`: a new, not-yet-listed value for an enum
|
|
metric - the state is exported as `UNKNOWN`, extend the list in the
|
|
source code if needed.
|