Quand on configure le forwarder JSON de Traccar (forward.type=json), le payload POSTé sur l’endpoint contient un objet position qui semble basique :
{
"position": {
"latitude": 47.20662,
"longitude": -1.55239,
"speed": 0,
"fixTime": "2026-05-30T08:40:23.000Z"
}
}
Sauf qu’il y a aussi un champ attributes qu’on néglige souvent à la première intégration :
"attributes": {
"sat": 21,
"rssi": 31,
"battery": 4.12,
"batteryLevel": 100,
"charge": false,
"motion": true,
"ignition": false,
"odometer": 0,
"totalDistance": 635.42,
"mode": 8,
"blocked": false,
"distance": 14.74
}
C’est là que les pépites se cachent.
Pourquoi c’est intéressant
Selon le protocole tracker, attributes contient potentiellement :
batteryLevel(%) oubattery(voltage Volts) → alerte batterie faiblesat→ nombre de satellites en vue (qualité du fix)rssi→ qualité du signal GSM/LTEmotion/ignition→ état mouvement/moteur (mode économie, véhicule)charge→ true si en charge (notif « branché »)totalDistance,odometer→ compteurs cumulésalarm→ type d’alarme JT808 (SOS, low battery firmware-side…)temperature,fuel→ capteurs externes véhicule
Le contenu exact varie selon le protocole (GT06, JT808, Teltonika, etc.). Traccar les normalise dans attributes.
Pattern de stockage flexible
Plutôt qu’une colonne par attribute possible, stocker tout en JSONB + extraire les fields-clés en colonnes dédiées pour les queries fréquentes :
ALTER TABLE positions
ADD COLUMN battery INT, -- extrait pour query rapide
ADD COLUMN attributes JSONB; -- tout le reste, queryable au besoin
Côté backend :
function extractBattery(attributes) {
if (!attributes) return null;
const lvl = attributes.batteryLevel;
if (typeof lvl === 'number' && lvl >= 0 && lvl <= 100) return Math.round(lvl);
const bat = attributes.battery; // fallback voltage si pas de %
if (typeof bat === 'number' && bat >= 0 && bat <= 100) return Math.round(bat);
return null;
}
INSERT INTO positions (..., battery, attributes)
VALUES (..., $7, $8); // [..., extractBattery(pos.attributes), JSON.stringify(pos.attributes)]
Bénéfices :
- Pas de migration à chaque nouveau tracker / protocole supporté
- Queries spécialisées sur
batteryrapides (colonne typed) - Inspection ad-hoc des autres fields via
attributes->>'sat'quand on en a besoin - Compatibilité automatique avec futurs trackers qui exposent des fields inconnus aujourd’hui
Lessons learned
position.attributesest le champ Traccar le plus sous-exploité par les intégrations naïves qui se contentent de lat/lon/speed- Le contenu varie selon le protocole — Traccar normalise les noms (
batteryLevel,sat,rssi…) mais tous les protocoles ne renseignent pas tous les fields - Stocker en JSONB + extraire les fields clés est plus flexible que des colonnes par attribute — supporte les nouveaux trackers sans migration
batteryLevelest en % (0-100),batteryest en voltage Volts — ne pas confondre, fallback intelligent côté codesat(nombre de satellites) est un bon proxy de qualité du fix : <4 = peu fiable, >8 = excellentcharge: truete dit quand le tracker est sur son socle de charge — utile pour notif « merci de débrancher » ou audit d’utilisation