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:
- Discord webhook — formatted embed with route name, delay, and a cached map image
- Gotify — push notification to Android for a quick glance without opening Discord
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
- Python — core application logic
- Paho MQTT — vehicle ignition event subscription
- Google Maps Routes API — real-time traffic data
- PostgreSQL — route and historical data persistence
- discord.py — bot interface and webhook alerts
- Gotify — Android push notifications