Automated Odometer Sync from Traccar to LubeLogger

Petter Aarak2 days ago

Automatic Odometer Synchronization from Traccar to LubeLogger

I've created a Python script that automatically updates LubeLogger with the latest odometer reading from a Traccar GPS tracker. The script fetches data from Traccar, compares it with the previous value in LubeLogger, and prevents updates if the new value is lower. This ensures accurate data in LubeLogger.

My Experience:

I'm not a programmer, but this script works great for me. I use an inexpensive GPS tracker that logs all trips to Traccar. The script runs automatically once a week (via cron) and updates the odometer in LubeLogger. This corrects small discrepancies between my manual fuel and mileage logs and the GPS data, ensuring my service reminders are accurate.

Key Features:

  • Fetches odometer data from the Traccar API.
  • Compares with the last recorded odometer reading in LubeLogger.
  • Prevents updates if the new value is lower.
  • Uses environment variables (.env) for easy configuration.
  • Supports optional logging (can be toggled on/off).

Setup:

  1. Edit the .env file with your API credentials and URLs.
  2. Run the script manually or set up a cron job for automatic synchronization.

Download:

You can find the Python script here: https://kutt.paa.no/nCjZy7

About LubeLogger:

For those unfamiliar with LubeLogger, it's a self-hosted, open-source vehicle maintenance and fuel tracking application. More information can be found by searching for "LubeLogger" online.

It doesn't look like your script does what you say it does. It doesn't connect to Traccar. Maybe you forgot to upload the latest version?

This is all you have now at the time of writing:

LUBELOGGER_LAST_ODOMETER_URL = os.getenv("LUBELOGGER_LAST_ODOMETER_URL")

def fetch_last_odometer():
    """Fetch the last recorded odometer from LubeLogger."""
    url = f"{LUBELOGGER_LAST_ODOMETER_URL}?vehicleId={LUBELOGGER_VEHICLE_ID}"
    log_debug(f"Fetching last odometer from: {url}")
    
    try:
        response = requests.get(url, auth=(LUBELOGGER_USER, LUBELOGGER_PASS))
        response.raise_for_status()
        last_odometer = response.text.strip()
        return int(last_odometer) if last_odometer.isdigit() else None
    except requests.exceptions.RequestException as e:
        log_error(f"Error fetching last odometer from LubeLogger: {e}")
        return None
Petter Aaraka day ago

Oops, I messed up and uploaded the wrong files. Should be fixed now, so try again! hope this works!

Petter Aaraka day ago

traccar_gps_lubelogger_odometer_update.py

import os
import requests
import logging
from datetime import datetime
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

# Configuration from .env
TRACCAR_URL = os.getenv("TRACCAR_URL")
TRACCAR_USER = os.getenv("TRACCAR_USER")
TRACCAR_PASS = os.getenv("TRACCAR_PASS")
TRACCAR_ID = os.getenv("TRACCAR_ID")

LUBELOGGER_POST_URL = os.getenv("LUBELOGGER_POST_URL")
LUBELOGGER_GET_URL = os.getenv("LUBELOGGER_GET_URL")
LUBELOGGER_USER = os.getenv("LUBELOGGER_USER")
LUBELOGGER_PASS = os.getenv("LUBELOGGER_PASS")
LUBELOGGER_VEHICLE_ID = os.getenv("LUBELOGGER_VEHICLE_ID")
LUBELOGGER_NOTES = os.getenv("LUBELOGGER_NOTES", "Auto update from Traccar")
LUBELOGGER_TAGS = os.getenv("LUBELOGGER_TAGS", "km traccar")

# Logging control
LUBELOGGER_SYNCLOG = os.getenv("LUBELOGGER_SYNCLOG", "enable").lower()

# Enable logging only if not disabled
if LUBELOGGER_SYNCLOG == "enable":
    logging.basicConfig(
        filename="odometer_sync.log",
        level=logging.DEBUG,
        format="%(asctime)s - %(levelname)s - %(message)s",
    )

def log_info(message):
    if LUBELOGGER_SYNCLOG == "enable":
        logging.info(message)
    print(message)

def log_debug(message):
    if LUBELOGGER_SYNCLOG == "enable":
        logging.debug(message)

def log_error(message):
    if LUBELOGGER_SYNCLOG == "enable":
        logging.error(message)
    print(f"Error: {message}")

def fetch_traccar_odometer():
    log_debug("Fetching data from Traccar")
    try:
        response = requests.get(TRACCAR_URL, auth=(TRACCAR_USER, TRACCAR_PASS))
        response.raise_for_status()
        data = response.json()
        for item in data:
            if item.get("deviceId") == int(TRACCAR_ID):
                return int(item.get("attributes", {}).get("totalDistance", 0) / 1000)
        log_error("Traccar vehicle ID not found!")
        return None
    except requests.exceptions.RequestException as e:
        log_error(f"Error fetching data from Traccar: {e}")
        return None

def fetch_last_odometer():
    log_debug("Fetching last odometer from LubeLogger")
    try:
        response = requests.get(f"{LUBELOGGER_GET_URL}?vehicleId={LUBELOGGER_VEHICLE_ID}", auth=(LUBELOGGER_USER, LUBELOGGER_PASS))
        response.raise_for_status()
        last_odometer = response.text.strip()
        return int(last_odometer) if last_odometer.isdigit() else None
    except requests.exceptions.RequestException as e:
        log_error(f"Error fetching last odometer from LubeLogger: {e}")
        return None

def update_lubelogger(odometer):
    last_odometer = fetch_last_odometer()
    if last_odometer is None:
        log_info("No previous odometer data found in LubeLogger. Using current odometer.")
        last_odometer = odometer
    if odometer < last_odometer:
        log_error(f"Odometer from Traccar ({odometer} km) is lower than last recorded ({last_odometer} km). Skipping update.")
        return
    data = {
        "vehicleId": LUBELOGGER_VEHICLE_ID,
        "date": datetime.today().strftime("%Y-%m-%d"),
        "initialOdometer": last_odometer,
        "odometer": odometer,
        "notes": LUBELOGGER_NOTES,
        "tags": LUBELOGGER_TAGS,
    }
    log_info("Sending data to LubeLogger")
    try:
        response = requests.post(
        LUBELOGGER_POST_URL,
            auth=(LUBELOGGER_USER, LUBELOGGER_PASS),
            data=data
        )
        response.raise_for_status()
        log_info(f"LubeLogger updated: {response.json()}")
    except requests.exceptions.RequestException as e:
        log_error(f"Error updating LubeLogger: {e}")

def main():
    odometer = fetch_traccar_odometer()
    if odometer is not None:
        log_info(f"Odometer from Traccar: {odometer} km")
        update_lubelogger(odometer)
    else:
        log_error("No odometer data available from Traccar.")

if __name__ == "__main__":
    main()     

Example .env

# Traccar API details
TRACCAR_URL=http://your-traccar-server/api/positions  # Traccar API endpoint
TRACCAR_USER=your-traccar-username  # Traccar API username
TRACCAR_PASS=your-traccar-password  # Traccar API password
TRACCAR_ID=1  # ID of the GPS tracker/vehicle in Traccar

# LubeLogger API details
LUBELOGGER_GET_URL=http://your-lubelogger-server/api/vehicle/odometerrecords/latest  # GET latest odometer record
LUBELOGGER_POST_URL=http://your-lubelogger-server/api/vehicle/odometerrecords/add  # POST new odometer record
LUBELOGGER_USER=your-lubelogger-username  # LubeLogger API username
LUBELOGGER_PASS=your-lubelogger-password  # LubeLogger API password
LUBELOGGER_VEHICLE_ID=1  # Vehicle ID in LubeLogger
LUBELOGGER_NOTES="Auto update from Traccar"  # Default note for entries
LUBELOGGER_TAGS="km traccar"  # Default tags for entries

# Enable/Disable logging (set to 'enable' or 'disable')
LUBELOGGER_SYNCLOG=enable