@oliverio
Ich häng mal das TypeScript dran, dann siehte ja:
// =============================================================================
// Konfiguration
// =============================================================================
const DP_LAT: string = 'traccar.0.devices.1.latitude';
const DP_LON: string = 'traccar.0.devices.1.longitude';
const DP_MOTION: string = 'traccar.0.devices.1.motion';
const DP_ROUTE_JSON: string = '0_userdata.0.CarLoad.CurrentRouteJSON';
const DP_LAST_DATE: string = '0_userdata.0.CarLoad.LastDriveDate';
const DP_LAST_STOP_TIME: string = '0_userdata.0.CarLoad.LastStopTime';
// Farben für die verschiedenen Fahrten des Tages (max 5)
const DRIVE_COLORS: string[] = ['#3d3d5c', '#e65c00', '#009933', '#cc0000', '#9900cc'];
// Interfaces für die neue verschachtelte Struktur
interface LeafletMarker {
latlng: [number, number];
options: {
icon: string;
title: string;
};
tooltip?: string;
}
interface LeafletIcon {
iconUrl: string;
iconSize: [number, number];
iconAnchor: [number, number];
popupAnchor?: [number, number];
shadowSize?: [number, number];
shadowAnchor?: [number, number];
}
interface PolylineConfig {
latlng: [number, number][];
options: { color: string; weight: number };
iobOptions?: { fitBounds: boolean };
}
interface MapContent {
marker: LeafletMarker[];
icons: {
honda: LeafletIcon;
};
polyline: PolylineConfig[];
}
interface DriveTrip {
map: MapContent;
}
interface LeafletRouteContainer {
[isoTimestamp: string]: DriveTrip;
}
// Erzeugt eine frische Map-Struktur für eine einzelne Fahrt
function createNewTripStructure(colorIndex: number): DriveTrip {
return {
map: {
marker: [
{
latlng: [0.0, 0.0],
options: {
icon: "honda",
title: "Default"
},
tooltip: "Hover me"
}
],
icons: {
honda: {
iconUrl: "/vis-2.0/SmartHome/tanken/honda_sv1.png",
iconSize: [45, 30],
iconAnchor: [15, 30],
popupAnchor: [0, -26],
shadowSize: [36, 36],
shadowAnchor: [15, 30]
}
},
polyline: [
{
latlng: [],
options: {
color: DRIVE_COLORS[colorIndex % DRIVE_COLORS.length],
weight: 3
},
iobOptions: {
fitBounds: true
}
}
]
}
};
}
// =============================================================================
// Initialisierung
// =============================================================================
async function init(): Promise<void> {
await createStateAsync(DP_LAST_DATE, '', { type: 'string' });
await createStateAsync(DP_LAST_STOP_TIME, 0, { type: 'number', name: 'Zeitstempel letzter Stop' });
if (!(await existsStateAsync(DP_ROUTE_JSON))) {
await createStateAsync(DP_ROUTE_JSON, '{}', {
name: 'Aktuelle Route für Leaflet',
type: 'string',
role: 'json'
});
}
}
// =============================================================================
// Logik: Koordinaten hinzufügen mit zeitbasierter Objekt-Struktur
// =============================================================================
async function addCoordinateToRoute(lat: number, lon: number): Promise<void> {
try {
const heute = new Date().toISOString().split('T')[0];
const lastDateState = await getStateAsync(DP_LAST_DATE);
const lastDate = lastDateState ? lastDateState.val : '';
const state = await getStateAsync(DP_ROUTE_JSON);
let routeData: LeafletRouteContainer = {};
// 1. Reset bei neuem Tag
if (lastDate !== heute) {
log(`[Traccar-Route] Neuer Tag erkannt. Reset der Route.`);
routeData = {};
await setStateAsync(DP_LAST_DATE, heute, true);
await setStateAsync(DP_LAST_STOP_TIME, 0, true);
} else if (state && typeof state.val === 'string' && state.val !== '') {
routeData = JSON.parse(state.val) as LeafletRouteContainer;
}
// Alle vorhandenen Keys (Fahrten) ermitteln und sortieren
let tripKeys = Object.keys(routeData).sort();
const lastStopState = await getStateAsync(DP_LAST_STOP_TIME);
const lastStopTime = lastStopState ? (lastStopState.val as number) : 0;
const jetzt = Date.now();
let currentTripKey: string;
// Prüfen, ob eine neue Fahrt begonnen werden muss
const isNewTripNeeded = tripKeys.length === 0 || (lastStopTime > 0 && (jetzt - lastStopTime) > (30 * 60 * 1000));
if (isNewTripNeeded) {
if (tripKeys.length < 5) {
currentTripKey = new Date().toISOString();
log(`[Traccar-Route] Erstelle neue Fahrt unter Key: ${currentTripKey}`);
routeData[currentTripKey] = createNewTripStructure(tripKeys.length);
tripKeys.push(currentTripKey);
} else {
log(`[Traccar-Route] Pause > 30 Min, aber Maximum von 5 Fahrten erreicht. Nutze letzte Fahrt weiter.`, 'warn');
currentTripKey = tripKeys[tripKeys.length - 1];
}
await setStateAsync(DP_LAST_STOP_TIME, 0, true);
} else {
currentTripKey = tripKeys[tripKeys.length - 1];
}
// Typisierte Referenz auf die aktive Fahrt holen
const activeTrip: MapContent = routeData[currentTripKey].map;
// 2. Live-Position des Markers aktualisieren
if (activeTrip.marker && activeTrip.marker.length > 0) {
activeTrip.marker[0].latlng = [lat, lon];
}
// 3. Polyline-Logik
const latlngs = activeTrip.polyline[0].latlng;
// Dubletten-Check innerhalb der aktuellen Fahrt
if (latlngs.length > 0) {
const lastPoint = latlngs[latlngs.length - 1];
if (lastPoint[0] === lat && lastPoint[1] === lon) return;
}
// Punkt hinzufügen
latlngs.push([lat, lon]);
// Performance-Schutz pro Fahrt
if (latlngs.length > 1500) latlngs.shift();
await setStateAsync(DP_ROUTE_JSON, JSON.stringify(routeData), true);
} catch (e) {
log(`[Leaflet-Route] Fehler beim Hinzufügen der Koordinate: ${e}`, 'error');
}
}
// =============================================================================
// Trigger
// =============================================================================
on({ id: DP_LAT, change: 'ne' }, async (obj) => {
const lat = obj.state ? parseFloat(obj.state.val as string) : null;
const lonState = await getStateAsync(DP_LON);
const motionState = await getStateAsync(DP_MOTION);
if (lat !== null && lonState && lonState.val !== null) {
const lon = parseFloat(lonState.val as string);
const isMoving = motionState ? !!motionState.val : true;
if (isMoving && !isNaN(lat) && !isNaN(lon)) {
await addCoordinateToRoute(lat, lon);
}
}
});
on({ id: DP_MOTION, change: 'ne' }, async (obj) => {
if (obj.state) {
const isMoving = !!obj.state.val;
if (!isMoving) {
log(`[Traccar-Route] Fahrzeug steht. Starte Haltezeit-Überwachung.`);
await setStateAsync(DP_LAST_STOP_TIME, Date.now(), true);
} else {
const lastStopState = await getStateAsync(DP_LAST_STOP_TIME);
const lastStopTime = lastStopState ? (lastStopState.val as number) : 0;
if (lastStopTime > 0 && (Date.now() - lastStopTime) <= (30 * 60 * 1000)) {
log(`[Traccar-Route] Fahrt nach kurzem Zwischenstopp (< 30 Min) fortgesetzt.`);
await setStateAsync(DP_LAST_STOP_TIME, 0, true);
}
}
}
});
init();