- README.md komplett neu geschrieben: - Fokus auf n8n-tracker.json (MQTT-only) - Entfernt: Telegram-Workflows, Datei-basierte Speicherung - Hinzugefügt: OwnTracks-Setup, Geräte-Mapping, erweiterte Features - Neue Sektionen: Sicherheitshinweise, DSGVO-Compliance - Praktische Code-Beispiele für Customization - CLAUDE.md aktualisiert: - Neue Workflow-Architektur dokumentiert - NocoDB-Schema mit battery/speed Feldern - Web-Interface Details (Filter, Kartenebenen, Marker) - Wichtige Gotchas und Edge Cases hinzugefügt - Dateien bereinigt: - Gelöscht: tracker.json, tracker-db.json, tracker-mqtt.json - Gelöscht: index_owntrack.html, locations-example.csv - Hinzugefügt: n8n-tracker.json (aktueller Workflow) - Hinzugefügt: database-example.csv (aktuelles Schema) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
13 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
This repository contains an MQTT-based location tracking system using n8n and NocoDB:
- n8n-tracker.json - MQTT/OwnTracks workflow with NocoDB storage
- index.html - Web interface with device filtering, time-based filtering, and multiple map layers
The system subscribes to MQTT topics (OwnTracks-compatible), processes location data, stores it in NocoDB, and provides both a REST API and web visualization.
Workflow Architecture
The n8n-tracker.json workflow contains two independent execution flows:
1. MQTT Location Capture Flow
MQTT Trigger (owntracks/#)
↓
MQTT Location verarbeiten (JavaScript: Parse JSON, transform data)
↓
Speichere in NocoDB (Create record with lat/lon, battery, speed, etc.)
Key nodes:
- MQTT Trigger: Subscribes to
owntracks/#topic, receives JSON messages from OwnTracks devices - MQTT Location verarbeiten:
- Parses the
messagefield (JSON string) - Validates required fields (lat, lon, tst)
- Transforms OwnTracks format to NocoDB schema
- Extracts telemetry data (battery, velocity, accuracy, altitude, etc.)
- Converts Unix timestamp to ISO 8601 format
- Parses the
- Speichere in NocoDB: Stores location with metadata in database
2. Location API Flow
Webhook - Location API (GET /webhook/location)
↓
Lade Daten aus NocoDB (Get all records)
↓
Format API Response (JavaScript: Sort, structure JSON)
↓
JSON Response (CORS-enabled)
Key nodes:
- Webhook - Location API: Public endpoint at
/webhook/locationwith CORS enabled - Lade Daten aus NocoDB: Fetches all location records from database
- Format API Response: Sorts by timestamp (newest first), builds response structure
- JSON Response: Returns structured JSON with CORS headers
Key Technical Details
Data Storage
NocoDB Database Configuration:
- Project ID:
pdxl4cx4dbu9nxi - Table ID:
m8pqj5ixgnnrzkg - Credential ID:
T9XuGr6CJD2W2BPO(NocoDB Token account) - Persistence: Full database persistence (no client-side limit)
NocoDB Schema
The database stores location records with the following fields:
latitude (Decimal) - Geographic latitude
longitude (Decimal) - Geographic longitude
timestamp (DateTime) - ISO 8601 timestamp
user_id (Number) - Always 0 for MQTT devices
first_name (Text) - Tracker ID (e.g., "10", "11")
last_name (Text) - Source type (e.g., "fused")
username (Text) - Same as tracker ID
marker_label (Text) - Display label for map markers
display_time (Text) - Formatted timestamp (de-DE locale)
chat_id (Number) - Always 0 for MQTT devices
battery (Number) - Battery percentage (0-100)
speed (Decimal) - Velocity in m/s
OwnTracks Data Mapping
The MQTT transformation node maps OwnTracks JSON fields to NocoDB schema:
| NocoDB Field | OwnTracks Field | Transformation |
|---|---|---|
latitude |
lat |
Direct mapping |
longitude |
lon |
Direct mapping |
timestamp |
tst |
Unix timestamp → ISO 8601 |
user_id |
- | Static: 0 |
first_name |
tid |
Tracker ID (device identifier) |
last_name |
source |
Location source (e.g., "fused") |
username |
tid |
Same as tracker ID |
marker_label |
tid |
Used for map display |
display_time |
tst |
Formatted with de-DE locale |
chat_id |
- | Static: 0 |
battery |
batt |
Battery percentage |
speed |
vel |
Velocity in m/s |
Additional OwnTracks data available but NOT stored:
acc- Accuracy in metersalt- Altitudecog- Course over groundconn- Connection type (w=WiFi, m=Mobile)_id- Device identifier
API Response Structure
{
"success": true,
"current": <most recent location object>,
"history": [<array of all location objects>],
"total_points": number,
"last_updated": "ISO 8601 string"
}
Web Interface (index.html)
The web interface is a single-page application built with Leaflet.js:
Configuration:
- API Endpoint:
https://n8n.unixweb.home64.de/webhook/location(line 178) - Default View: Munich (48.1351, 11.5820) at zoom level 12
- Auto-refresh: 5 second interval (configurable)
Key Features:
-
Multiple Map Layers (lines 158-171):
- Standard (OpenStreetMap)
- Satellite (Esri World Imagery)
- Terrain (OpenTopoMap)
- Dark Mode (CartoDB Dark)
-
Device Mapping (lines 142-152):
- Hardcoded device names:
'10'→ "Joachim Pixel",'11'→ "Huawei Smartphone" - Device-specific colors: Red (#e74c3c) for device 10, Blue (#3498db) for device 11
- Important: Device names are mapped from the
usernamefield (which contains the tracker ID)
- Hardcoded device names:
-
Filtering System:
- Device Filter: Dropdown populated dynamically from available
usernamevalues - Time Filter: 1h, 3h, 6h, 12h, 24h (default: 1 hour)
- Filter logic: Always filters to
user_id == 0(MQTT-only), then applies device and time filters
- Device Filter: Dropdown populated dynamically from available
-
Visualization (lines 284-376):
- Markers: Circular SVG icons with navigation-style clock hand
- Size: Latest location = 32x32px, history = 16x16px
- Colors: Device-specific colors from
DEVICE_COLORSmapping - Polylines: Shows movement path per device, color-coded
- Popups: Show device name, timestamp, battery %, speed (km/h)
Important Implementation Details:
- The
usernamefield filter logic (line 267) filters MQTT data by checkinguser_id == 0 - Device colors and names must be updated in the hardcoded mappings (lines 142-152)
- Speed conversion: OwnTracks velocity (m/s) is converted to km/h with
speed * 3.6(line 329)
Workflow Configuration (n8n-tracker.json)
Workflow Settings:
- Name: "Telegram Location Tracker - NocoDB"
- Workflow ID:
6P6dKqi4IKcJ521m - Version ID:
de17706a-a0ea-42ce-a069-dd09dce421d2 - Execution Order: v1
- Caller Policy: workflowsFromSameOwner
- Status:
active: true - Error Workflow:
0bBZzSE6SUzVsif5 - Tags: "owntrack"
Credentials:
- MQTT: Credential ID
L07VVR2BDfDda6Zo("MQTT account") - NocoDB: Credential ID
T9XuGr6CJD2W2BPO("NocoDB Token account")
Node Configuration:
-
MQTT Trigger:
- Topic:
owntracks/# - Subscribes to all OwnTracks topics
- No message filtering at trigger level (all messages pass through)
- Topic:
-
MQTT Location verarbeiten (Code Node):
- Parses JSON from
messagefield - Validates required fields:
lat,lon,tst - Skips invalid messages with
continue - Sets
alwaysOutputData: trueto handle empty results - Timezone: Europe/Berlin for
display_time
- Parses JSON from
-
Speichere in NocoDB:
- Operation:
create - Maps 12 fields from JSON to NocoDB columns
- Includes telemetry:
battery(frommqtt_data.battery),speed(frommqtt_data.velocity)
- Operation:
-
Webhook - Location API:
- Path:
/location - Webhook ID:
location-api-endpoint - Response Mode:
lastNode - CORS: Allowed origins =
*
- Path:
Common Modifications
Adding a New Device
Step 1: Update index.html device mappings (lines 142-152)
const DEVICE_NAMES = {
'10': 'Joachim Pixel',
'11': 'Huawei Smartphone',
'12': 'New Device Name' // Add this line
};
const DEVICE_COLORS = {
'10': '#e74c3c',
'11': '#3498db',
'12': '#2ecc71', // Add this line (green)
'default': '#95a5a6'
};
Step 2: Configure OwnTracks app
- Set Tracker ID (
tid) to match the key (e.g., "12") - Topic will be
owntracks/user/12 - The workflow automatically picks up new devices
Changing Date/Time Format
In n8n workflow node "MQTT Location verarbeiten" (line 124):
// Current: German format with Berlin timezone
const displayTime = new Date(timestampMs).toLocaleString('de-DE', { timeZone: 'Europe/Berlin' });
// Change to US format:
const displayTime = new Date(timestampMs).toLocaleString('en-US', { timeZone: 'America/New_York' });
// Change to ISO format:
const displayTime = new Date(timestampMs).toISOString();
Restricting CORS (Security)
In n8n workflow node "Webhook - Location API" (lines 65-75):
// Current (insecure):
{ "name": "Access-Control-Allow-Origin", "value": "*" }
// Change to specific domain:
{ "name": "Access-Control-Allow-Origin", "value": "https://web.unixweb.home64.de" }
Adding New NocoDB Fields
Example: Store accuracy and altitude
Step 1: Add columns in NocoDB:
accuracy(Number)altitude(Number)
Step 2: Update "MQTT Location verarbeiten" node (line 124):
mqtt_data: {
accuracy: mqttData.acc,
altitude: mqttData.alt,
battery: mqttData.batt,
velocity: mqttData.vel,
// ... existing fields
}
Step 3: Update "Speichere in NocoDB" node to map new fields:
{ "fieldName": "accuracy", "fieldValue": "={{ $json.mqtt_data.accuracy }}" },
{ "fieldName": "altitude", "fieldValue": "={{ $json.mqtt_data.altitude }}" }
Changing MQTT Topic Filter
In node "MQTT Trigger" (line 104):
// Current: All OwnTracks topics
topics: "owntracks/#"
// Change to specific user:
topics: "owntracks/joachim/#"
// Change to specific device:
topics: "owntracks/joachim/pixel"
// Multiple topics:
topics: "owntracks/joachim/#,owntracks/lisa/#"
Updating API Endpoint URL
In index.html (line 178):
// Current:
const API_URL = 'https://n8n.unixweb.home64.de/webhook/location';
// Change to your n8n instance:
const API_URL = 'https://your-n8n.example.com/webhook/location';
Changing Auto-Refresh Interval
In index.html (line 419):
// Current: 5 seconds
refreshInterval = setInterval(loadLocations, 5000);
// Change to 10 seconds:
refreshInterval = setInterval(loadLocations, 10000);
// Change to 30 seconds:
refreshInterval = setInterval(loadLocations, 30000);
Repository Contents
| File | Description |
|---|---|
n8n-tracker.json |
n8n workflow - MQTT location capture + API endpoint |
index.html |
Web interface with multi-layer maps and device filtering |
database-example.csv |
Sample NocoDB export showing actual data structure |
README.md |
Comprehensive German documentation (setup, usage, troubleshooting) |
CLAUDE.md |
This file - technical architecture documentation |
Important Gotchas and Edge Cases
1. Device Identification via username Field
- The
usernamefield contains the OwnTracks tracker ID (tid), not a username - The web interface filters devices by
username, not byfirst_nameormarker_label - Example: Device with
tid: "10"will haveusername: "10"in database - Device names are hardcoded in
index.html(lines 142-145) - must be manually updated
2. MQTT Message Validation
- The workflow does NOT filter by
_type: "location"despite what older documentation says - All MQTT messages are processed; validation happens in the JavaScript code node
- Messages missing
lat,lon, ortstare silently skipped withcontinue - Result: Non-location MQTT messages don't cause errors, they're just ignored
3. Time Filter Default
- The web interface defaults to 1 hour time filter (line 125)
- This means newly deployed users won't see historical data unless they change the filter
- Consider changing default to
24horallfor better initial experience
4. Circular Marker Icon Implementation
- Markers use SVG
divIconwith a navigation-style clock hand (lines 337-345) - The clock hand is purely decorative, does NOT represent actual direction/heading
- This replaced standard Leaflet pin icons in recent commits (see commit
4bec87d)
5. Speed Unit Conversion
- OwnTracks sends velocity in m/s (
velfield) - Stored in database as m/s in
speedcolumn - Converted to km/h in web UI with
speed * 3.6(line 329) - Important: If you display speed elsewhere, remember to convert
6. Battery Data May Be Null
- Not all OwnTracks messages include battery data
- The code checks for
battery !== undefined && battery !== nullbefore displaying (line 323) - Same applies to
speedfield (line 328)
7. CORS Configuration
- API has CORS set to
*(all origins allowed) - This is intentional for development but insecure for production
- See "Restricting CORS" section for how to fix
8. Error Workflow Reference
- The workflow references error workflow ID
0bBZzSE6SUzVsif5 - This error workflow is NOT included in the repository export
- If importing to a new n8n instance, errors will fail silently without this workflow
9. Timezone Handling
- All timestamps are converted to Europe/Berlin timezone (line 124 in workflow)
- This is hardcoded in the JavaScript transformation
- Database stores ISO 8601 UTC timestamps, but
display_timeis Berlin time