A self-hosted digital driving logbook for BMW vehicles, compliant with German tax authority (Tax Office) requirements. Connects to BMW's CarData MQTT streaming API for real-time telematics and automatic trip detection.
- Automatic trip detection from real-time GPS/location events via MQTT
- Real-time telematics – live vehicle data dashboard
- Trip management – classify trips as business or private
- Interactive maps – view trip routes on OpenStreetMap (Leaflet)
- Tax Office-compliant export – CSV and print-ready HTML/PDF
- Multi-vehicle support – manage multiple BMW vehicles
- German Tax Office requirements – all required fields included
- Automatic token refresh – stays connected without manual intervention
- Dark/light mode
| Component | Technology |
|---|---|
| Frontend | Nuxt 4 + @nuxt/ui + Tailwind CSS |
| Backend | Nuxt Nitro (Node.js) |
| Database | MongoDB + Mongoose |
| MQTT | mqtt npm package |
| Maps | Leaflet + OpenStreetMap |
| Auth | BMW OAuth 2.0 Device Code Flow + PKCE |
| Language | TypeScript |
- Node.js 20+
- pnpm 10+
- MongoDB 7.0+ (provide your own — local install, Atlas, or any MongoDB host)
- BMW CarData account with active subscriptions
git clone https://github.com/yourname/bmw-logbook.git
cd bmw-logbook
pnpm install
cp .env.example .env
# Set MONGODB_URI and NUXT_SESSION_SECRET in .env
pnpm build
pnpm previewFor development:
pnpm devAll BMW credentials are stored securely in MongoDB – never in environment variables or config files.
| Variable | Description | Required |
|---|---|---|
MONGODB_URI |
MongoDB connection string | Yes |
NUXT_SESSION_SECRET |
Secret for session signing (min. 32 chars) | Yes |
| Setting | Description |
|---|---|
| BMW CarData Client ID | From BMW customer portal |
| GCID | Your BMW account ID (auto-detected after auth) |
| MQTT Host | MQTT broker hostname |
| MQTT Port | MQTT broker port (typically 8883) |
| MQTT Topic Pattern | Topic to subscribe to (e.g., GCID/+) |
| Default Driver | Pre-filled driver name for new trips |
- Active BMW ConnectedDrive account with your vehicle mapped to it (you must be the primary user of the VIN)
- Vehicle with an active SIM card in a supported EU market
- Active ConnectedDrive contract
- Open the My BMW vehicle overview portal:
- BMW: https://www.bmw.de/de-de/mybmw/vehicle-overview (or your country's equivalent)
- Mini: https://www.mini.de/de_DE/home/mymini/vehicle-overview.html
- Click BMW CarData in the vehicle section
- Click "Create CarData Client" — this generates a unique Client ID. Copy it, you'll need it later.
- Subscribe to both services by clicking the respective buttons:
- CarData API → grants scope
cardata:api:read(vehicle info, REST API) - CarData Streaming → grants scope
cardata:streaming:read(real-time MQTT data)
- CarData API → grants scope
⚠️ Important: You must subscribe to the services before running the Device Code Flow in the app. If you authenticate first and subscribe later, the tokens will not include the required scopes and you'll need to re-authenticate.
-
Still in the BMW CarData section of the portal, click "Configure data stream"
-
You'll see a list of all available telematic data keys. Select the ones you need:
Required for automatic trip tracking:
Key Description vehicle.cabin.infotainment.navigation.currentLocation.latitudeGPS latitude vehicle.cabin.infotainment.navigation.currentLocation.longitudeGPS longitude Recommended:
Key Description vehicle.vehicle.travelledDistanceOdometer in km The full list of available keys is in the Telematics Data Catalogue (TDC) linked in the portal.
-
After saving your key selection, the portal shows your streaming credentials:
Parameter Description Example Host MQTT broker hostname mqtt.bmwgroup.comPort MQTT broker port 8883Topic Vehicle-specific topic (VIN-based) gcid12345abc/WBAXXXXXXXUsername Your GCID gcid12345abcThe password is your ID token (JWT) — the app obtains and refreshes this automatically.
- Open http://localhost:3000 and go to Settings
- In the BMW Authentication section:
- Enter the Client ID from Step 1
- Click "Start login"
- A short code is displayed — open the shown URL in your browser, log in with your BMW credentials, and enter the code
- The app polls automatically and saves the tokens once you've authorized
- After successful authentication, the GCID field is filled in automatically
- Still in Settings, fill in the MQTT Configuration section using the streaming credentials from Step 2:
- MQTT Host – broker hostname from the portal
- MQTT Port – typically
8883 - MQTT Username (GCID) – your GCID as shown in the portal
- MQTT Topic Pattern – use
your-gcid/#to receive all data for all your vehicles
- Click Save, then Reconnect
- The status badge should turn green (Connected)
Note: BMW allows only one active MQTT connection per GCID at a time. If you connect from multiple devices, the previous connection will be dropped.
- Go to Vehicles
- Click "Sync from BMW" — this loads your mapped vehicles from the CarData API automatically
- For each vehicle, set the MQTT Topic to the vehicle-specific topic from Step 2 (e.g.
gcid12345abc/WBAXXXXXXX) - Enter the license plate — required for the Tax Office export
- Driver name – set your default driver in Settings → General Settings
- Trip purposes – classify each trip as Business or Private in the Trips page
- License plates – must be set on each vehicle
Trips are detected automatically when your vehicle moves more than 100m. After 10 minutes of inactivity, the trip is automatically closed.
To classify a trip:
- Go to Trips
- Click the classify dropdown on any trip
- Select: Business or Private
- For business trips, add the destination and contact in the trip detail view
- Go to Export
- Select vehicle and year
- Ensure all trips are classified
- Download as CSV (for Excel/tax advisor) or open PDF view for printing
This app generates exports that comply with the requirements of the German Federal Ministry of Finance (BMF) for electronic driving logs.
The export includes all required fields per § 6 Abs. 1 Nr. 4 EStG:
- Date of each trip
- Start and end location with full address
- Distance in km
- Odometer readings at start and end
- Purpose of trip with classification
- Business destination and contact for business trips
- Driver name
- Vehicle data: make, model, license plate, VIN
- Annual totals: total km, business km, private km
Note: The Tax Office requires continuous recording without gaps. Ensure your MQTT connection remains active while the vehicle is in use.
Before submitting your logbook to the tax authority, verify:
- All trips are classified (no "Unclassified" trips remain)
- Business trips have a destination/purpose filled in
- All trips have a driver name
- Vehicle has a license plate set
- The year's trips are complete (no gaps)
- Export the CSV or PDF for the relevant tax year
POST /api/auth/device-code– Start Device Code FlowPOST /api/auth/token– Exchange device code for tokensPOST /api/auth/refresh– Refresh tokens
GET /api/settings– Get settings (no tokens exposed)PUT /api/settings– Update settings
GET /api/vehicles– List vehiclesPOST /api/vehicles– Add vehicleGET /api/vehicles/bmw-mappings– Sync from BMW APIPATCH /api/vehicles/:vin– Update vehicleDELETE /api/vehicles/:vin– Remove vehicleGET /api/vehicles/:vin/live– Live telematics
GET /api/trips– List trips (paginated, filterable)POST /api/trips– Create manual tripGET /api/trips/:id– Trip details with raw eventsPATCH /api/trips/:id– Update tripDELETE /api/trips/:id– Delete tripGET /api/trips/export?format=csv|html– Export
GET /api/stats– Aggregate statistics
GET /api/mqtt/status– Connection statusPOST /api/mqtt/reconnect– Trigger reconnect
┌─────────────────────────────────────────────────┐
│ Browser (Vue 3) │
│ Dashboard | Trips | Vehicles | Live | Settings │
└─────────────────────┬───────────────────────────┘
│ HTTP
┌─────────────────────▼───────────────────────────┐
│ Nuxt Nitro (Server) │
│ ┌────────────────┐ ┌────────────────────────┐ │
│ │ REST API │ │ MQTT Plugin │ │
│ │ /api/... │ │ ─ Connect to BMW │ │
│ │ │ │ ─ Parse messages │ │
│ │ │ │ ─ Store RawEvents │ │
│ │ │ │ ─ Trip Detector │ │
│ └────────────────┘ └────────────────────────┘ │
│ ┌──────────────────────────────────────────────┐ │
│ │ MongoDB (Mongoose) │ │
│ │ Settings | Vehicle | RawEvent | Trip │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────────────────────────┬────────────┘
│ MQTT (TLS)
┌────────────────▼──────────┐
│ BMW CarData MQTT Broker │
│ Real-time telematics │
└───────────────────────────┘
MQTT not connecting:
- Verify host, port, and topic pattern are correct
- Check that the ID token hasn't expired (Settings shows expiry)
- Try clicking "Reconnect" after saving settings
- The ID token is automatically refreshed 15 minutes before expiry
No trips being recorded:
- Check MQTT status is green (connected)
- Verify your vehicle's MQTT topic matches what you entered
- Check that the telematic keys are enabled in the BMW portal
BMW API errors (403/401):
- Your access token may have expired – click "Refresh token manually"
- Check your subscriptions are active in the BMW portal
- Fork the repository
- Create a feature branch (
git checkout -b feature/your-feature) - Commit your changes (
git commit -m 'Add your feature') - Push to the branch (
git push origin feature/your-feature) - Open a Pull Request
Please follow the existing code style (TypeScript, ESLint) and add appropriate error handling.
MIT License – see LICENSE for details.
This project is not affiliated with, endorsed by, or connected to BMW AG. BMW, BMW ConnectedDrive, and BMW CarData are trademarks of BMW AG. Use of the BMW CarData API requires an active subscription from BMW AG.
The user is responsible for ensuring their use of the BMW CarData API complies with BMW's terms of service.