Add persistent car performance analytics

This commit is contained in:
2026-07-31 22:44:51 +02:00
parent 0e4ce931ec
commit 6998e8bdb7
12 changed files with 916 additions and 29 deletions
+19 -2
View File
@@ -30,12 +30,12 @@ import {
} from "./network.js";
import {
collectAdbStatus,
collectCarTelemetry,
installAdbApk,
launchAdbTarget,
listInstalledAdbPackages,
MAX_APK_BYTES
} from "./adb.js";
import { CarTelemetryRecorder, type TelemetryPeriod } from "./car-telemetry.js";
import { openShell } from "./shell.js";
import { systemPowerActionSchema, triggerSystemPower } from "./system.js";
import "./types.js";
@@ -139,7 +139,9 @@ export async function buildApp(config: AppConfig): Promise<FastifyInstance> {
});
const database = openDatabase(config.databasePath);
const jobRunner = new JobRunner(database, undefined, config.adb);
const carTelemetry = new CarTelemetryRecorder(database, config.adb);
if (config.adb.enabled) void collectAdbStatus(config.adb);
carTelemetry.start();
app.decorate("database", database);
app.decorate("jobRunner", jobRunner);
@@ -154,6 +156,7 @@ export async function buildApp(config: AppConfig): Promise<FastifyInstance> {
);
app.addHook("onClose", async () => {
carTelemetry.stop();
jobRunner.close();
database.close();
});
@@ -482,12 +485,26 @@ export async function buildApp(config: AppConfig): Promise<FastifyInstance> {
app.get("/api/car/telemetry", async (request, reply) => {
if (!requireUser(request, reply)) return;
try {
return await collectCarTelemetry(config.adb);
return await carTelemetry.readLive();
} catch {
return reply.code(503).send({ error: { code: "CAR_TELEMETRY_UNAVAILABLE", message: "Live car telemetry is unavailable over ADB" } });
}
});
app.get<{ Querystring: { period?: string } }>("/api/car/history", async (request, reply) => {
if (!requireUser(request, reply)) return;
const parsed = z.enum(["24h", "7d", "30d", "1y", "all"]).safeParse(request.query.period ?? "24h");
if (!parsed.success) {
return reply.code(400).send({ error: { code: "INVALID_HISTORY_PERIOD", message: "That history period is not available" } });
}
return carTelemetry.store.history(parsed.data as TelemetryPeriod);
});
app.get("/api/car/statistics", async (request, reply) => {
if (!requireUser(request, reply)) return;
return carTelemetry.store.overview();
});
app.post(
"/api/network/configuration",
{ config: { rateLimit: { max: 3, timeWindow: "10 minutes" } } },