Building a Real-Time Traffic Monitor with Google Maps and MQTT

This one started as a simple question: why am I always surprised by traffic? I drive the same routes every day. I have a vehicle with a GPS tracker. I have a server running 24/7. There was no reason I shouldn't be getting a heads-up before leaving — and no reason I needed a subscription service to do it.

Traffic Manager is what came out of that frustration. It monitors road conditions on predefined routes using the Google Maps Routes API, triggers alerts through Discord and Gotify, and ties it all together with MQTT messages from my vehicle's ignition system.

How it works

The system is event-driven. When my vehicle's ignition turns on, a GPS tracker publishes an MQTT message to a local broker. Traffic Manager subscribes to that topic and uses it as the trigger to run a traffic check — no point querying traffic at 2am when you're not driving.

# Simplified MQTT listener
client.subscribe("vehicle/+/ignition")

def on_message(client, userdata, msg):
    payload = json.loads(msg.payload)
    if payload["state"] == "on":
        check_all_routes()

Each route is stored in PostgreSQL with a name, priority level, and a list of waypoints in DMS (Degrees Minutes Seconds) coordinates. When a check is triggered, the system queries the Google Maps Routes API for each route and evaluates the result against a dynamic threshold.

Dynamic thresholds

This was the trickiest part. A five-minute delay on a 10-minute commute is a big deal. The same delay on a 90-minute journey is barely worth mentioning. A fixed "alert if delay exceeds X minutes" threshold doesn't hold up across different route lengths.

The solution was to calculate thresholds proportionally based on route distance and typical travel time. The system also analyses individual segments, not just the total journey, so it can distinguish between one bad bottleneck and distributed light congestion.

Alert routing

Routes are flagged as either high priority or normal priority. High-priority routes always generate alerts when conditions are bad. Normal-priority routes only alert if the delay is significant — this prevents notification fatigue for optional commutes.

Alerts go out through two channels simultaneously:

The Discord bot side

Beyond automated alerts, there's an interactive Discord bot for managing the system without touching the database directly. It uses modals for adding routes, lets you update priority levels, check current traffic on demand, and adjust detection sensitivity — all from a Discord slash command.

The biggest lesson: designing for your own use case makes a system dramatically better than a generic solution. I know my routes, I know my priorities, and the system reflects that exactly.

Stack


← Back to all posts  ·  View on GitHub ↗