Skip to main content
Glama
keithah

Tessie MCP Extension

by keithah
drive-analyzer.ts13.6 kB
import { TessieDrive } from './tessie-client.js'; export interface MergedDrive { id: string; // Composite ID of merged drives originalDriveIds: number[]; started_at: number; ended_at: number; starting_location: string; ending_location: string; starting_battery: number; ending_battery: number; total_distance: number; total_duration_minutes: number; driving_duration_minutes: number; stops: DriveStop[]; energy_consumed: number; average_speed: number; max_speed: number; } export interface DriveStop { location: string; duration_minutes: number; stop_type: 'short' | 'charging' | 'excluded'; started_at: number; ended_at: number; } export interface DriveAnalysis { mergedDrive: MergedDrive; batteryConsumption: { percentage_used: number; estimated_kwh_used: number; efficiency_miles_per_kwh?: number; }; fsdAnalysis: { total_autopilot_miles: number; fsd_percentage: number; note?: string; }; summary: string; } export class DriveAnalyzer { /** * Merges consecutive drives that are separated by stops less than 7 minutes * or charging stops, treating them as a single continuous journey */ mergeDrives(drives: TessieDrive[]): MergedDrive[] { if (drives.length === 0) return []; // Sort drives by start time const sortedDrives = [...drives].sort((a, b) => a.started_at - b.started_at); const mergedDrives: MergedDrive[] = []; let currentGroup: TessieDrive[] = [sortedDrives[0]]; for (let i = 1; i < sortedDrives.length; i++) { const prevDrive = sortedDrives[i - 1]; const currentDrive = sortedDrives[i]; // Calculate gap between drives const gapMinutes = (currentDrive.started_at - prevDrive.ended_at) / 60; // Check if this should be merged with the previous group if (this.shouldMergeDrives(prevDrive, currentDrive, gapMinutes)) { currentGroup.push(currentDrive); } else { // Process the current group and start a new one mergedDrives.push(this.createMergedDrive(currentGroup)); currentGroup = [currentDrive]; } } // Process the final group if (currentGroup.length > 0) { mergedDrives.push(this.createMergedDrive(currentGroup)); } return mergedDrives; } private shouldMergeDrives(prevDrive: TessieDrive, currentDrive: TessieDrive, gapMinutes: number): boolean { // Merge if gap is less than 7 minutes if (gapMinutes < 7) { return true; } // TODO: Add charging detection logic // This would require checking if the gap includes charging based on battery level changes // For now, we'll use a simple heuristic: if battery level increased significantly during the gap const batteryIncrease = currentDrive.starting_battery - prevDrive.ending_battery; if (batteryIncrease > 5) { // More than 5% battery increase suggests charging return true; } return false; } private createMergedDrive(drives: TessieDrive[]): MergedDrive { if (drives.length === 0) { throw new Error('Cannot create merged drive from empty array'); } const firstDrive = drives[0]; const lastDrive = drives[drives.length - 1]; // Calculate stops between drives const stops: DriveStop[] = []; for (let i = 0; i < drives.length - 1; i++) { const current = drives[i]; const next = drives[i + 1]; const gapMinutes = (next.started_at - current.ended_at) / 60; if (gapMinutes > 0) { const batteryChange = next.starting_battery - current.ending_battery; const stopType = batteryChange > 5 ? 'charging' : gapMinutes < 7 ? 'short' : 'excluded'; stops.push({ location: current.ending_location, duration_minutes: Math.round(gapMinutes * 100) / 100, stop_type: stopType, started_at: current.ended_at, ended_at: next.started_at }); } } // Calculate totals const totalDistance = drives.reduce((sum, drive) => sum + drive.odometer_distance, 0); const totalDuration = (lastDrive.ended_at - firstDrive.started_at) / 60; // in minutes const drivingDuration = drives.reduce((sum, drive) => { return sum + ((drive.ended_at - drive.started_at) / 60); }, 0); // Calculate speeds const maxSpeed = Math.max(...drives.map(d => d.max_speed || 0)); const averageSpeed = totalDistance > 0 ? (totalDistance / (drivingDuration / 60)) : 0; // Create initial merged drive without autopilot data (will be predicted later) const mergedDrive = { id: `merged_${drives.map(d => d.id).join('_')}`, originalDriveIds: drives.map(d => d.id), started_at: firstDrive.started_at, ended_at: lastDrive.ended_at, starting_location: firstDrive.starting_location, ending_location: lastDrive.ending_location, starting_battery: firstDrive.starting_battery, ending_battery: lastDrive.ending_battery, total_distance: Math.round(totalDistance * 100) / 100, total_duration_minutes: Math.round(totalDuration * 100) / 100, driving_duration_minutes: Math.round(drivingDuration * 100) / 100, stops, autopilot_percentage: 0, // Will be predicted in analyzeFSDUsage energy_consumed: firstDrive.starting_battery - lastDrive.ending_battery, average_speed: Math.round(averageSpeed * 100) / 100, max_speed: Math.round(maxSpeed * 100) / 100 }; // Predict autopilot usage for this merged drive const predictedAutopilotMiles = this.predictAutopilotUsage(mergedDrive); return mergedDrive; } /** * Analyzes the most recent merged drive with comprehensive metrics */ analyzeLatestDrive(drives: TessieDrive[]): DriveAnalysis | null { if (drives.length === 0) return null; const mergedDrives = this.mergeDrives(drives); if (mergedDrives.length === 0) return null; // Get the most recent merged drive const latestMerged = mergedDrives[mergedDrives.length - 1]; // Calculate battery consumption analysis const batteryConsumption = this.analyzeBatteryConsumption(latestMerged); // Calculate FSD analysis const fsdAnalysis = this.analyzeFSDUsage(latestMerged); // Generate summary const summary = this.generateDriveSummary(latestMerged, batteryConsumption, fsdAnalysis); return { mergedDrive: latestMerged, batteryConsumption, fsdAnalysis, summary }; } private analyzeBatteryConsumption(drive: MergedDrive) { const percentageUsed = Math.round((drive.energy_consumed) * 100) / 100; // Estimate kWh usage (rough Tesla Model 3/Y approximation: ~75-100kWh total capacity) // This is an approximation - actual capacity varies by model and year const estimatedTotalCapacity = 75; // kWh - conservative estimate const estimatedKwhUsed = Math.round((percentageUsed / 100) * estimatedTotalCapacity * 100) / 100; const efficiency = drive.total_distance > 0 && estimatedKwhUsed > 0 ? Math.round((drive.total_distance / estimatedKwhUsed) * 100) / 100 : undefined; return { percentage_used: percentageUsed, estimated_kwh_used: estimatedKwhUsed, efficiency_miles_per_kwh: efficiency }; } private analyzeFSDUsage(drive: MergedDrive) { // Predict FSD usage based on driving patterns since Tessie API doesn't provide autopilot data const predictedAutopilotMiles = this.predictAutopilotUsage(drive); const predictedPercentage = drive.total_distance > 0 ? Math.round((predictedAutopilotMiles / drive.total_distance) * 10000) / 100 : 0; return { total_autopilot_miles: predictedAutopilotMiles, fsd_percentage: predictedPercentage, note: predictedAutopilotMiles > 0 ? "Estimated based on highway driving patterns and speed consistency" : "Low probability of FSD usage detected from driving patterns" }; } /** * Predicts autopilot usage based on driving patterns * Factors: highway speeds, long distance, speed consistency, duration */ predictAutopilotUsage(drive: MergedDrive): number { if (drive.total_distance < 2) { // Very short drives unlikely to use autopilot return 0; } let autopilotLikelihood = 0; // Factor 1: Highway speed indicator (adjusted for real-world highway averages) if (drive.average_speed > 35) { autopilotLikelihood += 0.5; // Strong highway indicator } else if (drive.average_speed > 25) { autopilotLikelihood += 0.3; // Moderate highway/arterial indicator } else if (drive.average_speed > 15) { autopilotLikelihood += 0.1; // Light highway segments } // Factor 2: Distance-based likelihood (more realistic for FSD usage patterns) if (drive.total_distance > 15) { autopilotLikelihood += 0.4; // Long drives - very likely FSD } else if (drive.total_distance > 8) { autopilotLikelihood += 0.3; // Medium drives - likely FSD } else if (drive.total_distance > 4) { autopilotLikelihood += 0.2; // Short highway segments } // Factor 3: Max speed indicator (shows highway capability) if (drive.max_speed > 65) { autopilotLikelihood += 0.2; // Highway speeds } else if (drive.max_speed > 45) { autopilotLikelihood += 0.1; // Arterial speeds } // Factor 4: Speed consistency (autopilot tends to maintain steady speeds) const speedConsistency = this.calculateSpeedConsistency(drive); autopilotLikelihood += speedConsistency * 0.15; // Factor 5: Duration factor (longer drives more likely to use autopilot) if (drive.driving_duration_minutes > 20) { autopilotLikelihood += 0.15; // Extended driving } else if (drive.driving_duration_minutes > 10) { autopilotLikelihood += 0.1; // Medium duration } // Cap likelihood and apply more realistic highway assumptions autopilotLikelihood = Math.min(autopilotLikelihood, 1.0); // Boost likelihood for clear highway patterns but keep realistic if (drive.average_speed > 40 && drive.total_distance > 20) { autopilotLikelihood = Math.max(autopilotLikelihood, 0.75); // Clear highway drive } else if (drive.average_speed > 30 && drive.total_distance > 10) { autopilotLikelihood = Math.max(autopilotLikelihood, 0.65); // Likely highway drive } // Apply realistic deductions for non-FSD portions if (drive.total_distance > 5) { // Deduct estimated parking/city driving at start and end (~1-2 miles total) const parkingDeduction = Math.min(2.0 / drive.total_distance, 0.15); autopilotLikelihood = Math.max(0, autopilotLikelihood - parkingDeduction); } // Cap maximum realistic FSD usage (even perfect highway drives have some manual portions) autopilotLikelihood = Math.min(autopilotLikelihood, 0.92); // Max 92% FSD usage // Calculate estimated autopilot miles const estimatedAutopilotMiles = drive.total_distance * autopilotLikelihood; return Math.round(estimatedAutopilotMiles * 100) / 100; } /** * Calculate speed consistency score (0-1, where 1 is perfectly consistent) */ calculateSpeedConsistency(drive: MergedDrive): number { // Simple heuristic: if max speed isn't much higher than average, // it suggests consistent speeds (typical of autopilot) if (drive.average_speed === 0 || drive.max_speed === 0) return 0; const speedRatio = drive.average_speed / drive.max_speed; // If average is close to max speed, it's very consistent if (speedRatio > 0.85) return 1.0; if (speedRatio > 0.75) return 0.8; if (speedRatio > 0.65) return 0.6; if (speedRatio > 0.55) return 0.4; return 0.2; } private generateDriveSummary( drive: MergedDrive, battery: { percentage_used: number; estimated_kwh_used: number; efficiency_miles_per_kwh?: number }, fsd: { total_autopilot_miles: number; fsd_percentage: number; note?: string } ): string { const duration = drive.total_duration_minutes; const drivingTime = drive.driving_duration_minutes; const stopTime = duration - drivingTime; let summary = `Drive from ${drive.starting_location} to ${drive.ending_location}:\n`; summary += `• Total time: ${Math.floor(duration / 60)}h ${Math.round(duration % 60)}m\n`; summary += `• Driving time: ${Math.floor(drivingTime / 60)}h ${Math.round(drivingTime % 60)}m\n`; if (stopTime > 1) { summary += `• Stop time: ${Math.floor(stopTime / 60)}h ${Math.round(stopTime % 60)}m`; if (drive.stops.length > 0) { const chargingStops = drive.stops.filter(s => s.stop_type === 'charging').length; const shortStops = drive.stops.filter(s => s.stop_type === 'short').length; if (chargingStops > 0) summary += ` (${chargingStops} charging stop${chargingStops > 1 ? 's' : ''})`; if (shortStops > 0) summary += ` (${shortStops} short stop${shortStops > 1 ? 's' : ''})`; } summary += `\n`; } summary += `• Distance: ${drive.total_distance} miles\n`; summary += `• Average speed: ${drive.average_speed} mph (max: ${drive.max_speed} mph)\n`; summary += `• Battery used: ${battery.percentage_used}% (≈${battery.estimated_kwh_used} kWh)\n`; if (battery.efficiency_miles_per_kwh) { summary += `• Efficiency: ${battery.efficiency_miles_per_kwh} mi/kWh\n`; } if (fsd.total_autopilot_miles > 0) { summary += `• FSD/Autopilot: ${fsd.total_autopilot_miles} miles (${fsd.fsd_percentage}% of drive)`; } else { summary += `• FSD/Autopilot: ${fsd.note || 'Data not available'}`; } return summary; } }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/keithah/tessie-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server