inital impl
This commit is contained in:
@@ -1,3 +1,134 @@
|
||||
# vw-eu-data-act-prometheus-exporter
|
||||
|
||||
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, raw JSON datasets can be persisted to disk via
|
||||
`config.persist_raw_json` (see [Configuration](#configuration-configpy))
|
||||
- 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 |
|
||||
|
||||
**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
|
||||
sudo mkdir -p /opt/vw-eu-data-act-exporter
|
||||
sudo useradd --system --no-create-home --shell /usr/sbin/nologin vw-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 vw-exporter:vw-exporter /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 raw JSON of every downloaded dataset is written to disk (one file per VIN per scrape) - 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.
|
||||
|
||||
Reference in New Issue
Block a user