Code review fixes: app 2.9.0, server 2.2.0
Telemetry no longer loses queued batches. The server drops a bad value, or a sample without a plausible time, instead of rejecting the batch, and answers 200 with what it dropped. The app sends only values inside the server's ranges (speed as a magnitude, so reversing is fine) and no samples before the head unit's clock is set. Server: - Pairing hands out and accepts a token only after the device is saved; a failed save leaves the request pending. - A paired token serves only the installation id it was paired with (403 otherwise); the shared INGEST_TOKEN still serves any. - Wrong-password lockouts and open pairing requests are limited per client address (last X-Forwarded-For entry), so a stranger cannot lock the owner out; a car starting again replaces its own request. - ABRP forwarding skips samples stamped ahead of the server's clock, which used to stop it until a restart. App: - Settings written to plaintext during a keystore failure move back into encrypted storage; the stored installation id wins. - The installation id is derived from ANDROID_ID on first start, so a reinstall finds its history; a stored or imported id still wins. - The scanned config file is deleted after import (it holds the token). - GPS stops while live telemetry is off or the server unconfigured, and pauses while parked and idle, keeping the fix the car parked with. - Range estimates use the monotonic clock. - One HTTP helper (ServerHttp) for telemetry, sync and pairing. - Last Turkish strings translated; mise logs shows the NilsMG4 tags. Grafana reads InfluxDB with its own read-only token (new grafana-influx Secret, created by hand as described in k8s/README.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
This commit is contained in:
@@ -64,7 +64,8 @@ final class TelemetryPayload {
|
||||
tlm.put("utc", utc);
|
||||
|
||||
putIfPresent(tlm, "soc", soc);
|
||||
putIfPresent(tlm, "speed", speedKmh == null ? null : Math.round(speedKmh));
|
||||
// Reversing reads negative; ABRP and the server take speed as a magnitude.
|
||||
putIfPresent(tlm, "speed", speedKmh == null ? null : Math.round(Math.abs(speedKmh)));
|
||||
putIfPresent(tlm, "est_battery_range", rangeKm);
|
||||
if (powerKw != null) tlm.put("power", round2(powerKw));
|
||||
putIfPresent(tlm, "is_charging", boolToInt(charging));
|
||||
@@ -134,9 +135,62 @@ final class TelemetryPayload {
|
||||
// already exclude. Returning just the timestamp keeps the upload alive.
|
||||
return "{\"utc\":" + utc + "}";
|
||||
}
|
||||
dropOutOfRange(tlm);
|
||||
return tlm.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* The server's range for every numeric field (server/model.go, telemetryNumbers). A value
|
||||
* outside it is left out here, as an unread one would be: the server would drop it anyway,
|
||||
* and an older server rejects the whole batch for it.
|
||||
*/
|
||||
static final java.util.Map<String, double[]> SERVER_LIMITS = new java.util.HashMap<>();
|
||||
static {
|
||||
SERVER_LIMITS.put("soc", new double[]{0, 100});
|
||||
SERVER_LIMITS.put("speed", new double[]{0, 400});
|
||||
SERVER_LIMITS.put("power", new double[]{-1000, 1000});
|
||||
SERVER_LIMITS.put("est_battery_range", new double[]{0, 2000});
|
||||
SERVER_LIMITS.put("capacity", new double[]{0, 500});
|
||||
SERVER_LIMITS.put("soe", new double[]{0, 500});
|
||||
SERVER_LIMITS.put("kwh_charged", new double[]{0, 1000});
|
||||
SERVER_LIMITS.put("odometer", new double[]{0, 10_000_000});
|
||||
SERVER_LIMITS.put("lat", new double[]{-90, 90});
|
||||
SERVER_LIMITS.put("lon", new double[]{-180, 180});
|
||||
SERVER_LIMITS.put("elevation", new double[]{-1000, 10_000});
|
||||
SERVER_LIMITS.put("heading", new double[]{0, 360});
|
||||
SERVER_LIMITS.put("batt_temp", new double[]{-60, 100});
|
||||
SERVER_LIMITS.put("cabin_temp", new double[]{-60, 100});
|
||||
SERVER_LIMITS.put("ext_temp", new double[]{-60, 70});
|
||||
SERVER_LIMITS.put("hvac_setpoint", new double[]{0, 40});
|
||||
SERVER_LIMITS.put("voltage", new double[]{0, 1500});
|
||||
SERVER_LIMITS.put("current", new double[]{-2000, 2000});
|
||||
SERVER_LIMITS.put("tire_pressure_fl", new double[]{0, 1000});
|
||||
SERVER_LIMITS.put("tire_pressure_fr", new double[]{0, 1000});
|
||||
SERVER_LIMITS.put("tire_pressure_rl", new double[]{0, 1000});
|
||||
SERVER_LIMITS.put("tire_pressure_rr", new double[]{0, 1000});
|
||||
}
|
||||
|
||||
private static void dropOutOfRange(JSONObject tlm) {
|
||||
for (java.util.Map.Entry<String, double[]> limit : SERVER_LIMITS.entrySet()) {
|
||||
String key = limit.getKey();
|
||||
if (!tlm.has(key)) continue;
|
||||
double value = tlm.optDouble(key, Double.NaN);
|
||||
if (Double.isNaN(value) || value < limit.getValue()[0] || value > limit.getValue()[1]) tlm.remove(key);
|
||||
}
|
||||
// A position is only a position as a pair.
|
||||
if (tlm.has("lat") != tlm.has("lon")) {
|
||||
tlm.remove("lat");
|
||||
tlm.remove("lon");
|
||||
}
|
||||
}
|
||||
|
||||
/** Epoch seconds the server accepts: from 2001. Earlier means the head unit's clock is not set yet. */
|
||||
static final long MIN_UTC = 1_000_000_000L;
|
||||
|
||||
static boolean isPlausibleUtc(long utcSeconds) {
|
||||
return utcSeconds >= MIN_UTC;
|
||||
}
|
||||
|
||||
/**
|
||||
* True for a temperature that a sensor could actually have measured. Excludes exactly
|
||||
* 0.0, which is what an unimplemented VHAL property returns — see cabin_temp above.
|
||||
|
||||
Reference in New Issue
Block a user