Files
mg4/app/src/main/java/com/evsuite/abrp/AbrpApi.java
T
nilsandClaude Opus 5.5 ca2f899403 App 2.11.0, server 2.3.0: live data straight to ABRP; settings regrouped
ABRP keeps getting live data while the car server is down: with an ABRP
API key and user token set in the app (Live data tab, or abrp_api_key /
abrp_token in the config file), the car sends the newest sample to ABRP
itself at every upload. It tells the server the newest sample ABRP took
(abrpSentUtc), and the server forwards only newer ones, so ABRP gets
each sample once. Without credentials in the app the server forwards
everything, as before. The credentials sit in encrypted storage, go in
the request body, and neither they nor ABRP's answer are logged.

Settings are grouped by what they are for:
- Live data (was Upload): on/off, ABRP credentials, upload frequency.
- Settings (replaces the Wi-Fi tab): full screen and voice key,
  usable capacity and manual SoH (were on Consumption), charging price
  (was on Charging), Wi-Fi.
Charging and Live data get icons of their own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
2026-10-18 12:15:21 +02:00

60 lines
2.5 KiB
Java

package com.evsuite.abrp;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
* ABRP's telemetry API, for sending live data straight from the car.
*
* Credentials go in the POST body, never in the query string: a URL travels through proxies,
* access logs and crash reports, a body does not. Neither is ever logged.
*/
final class AbrpApi {
/** No query string: credentials travel in the body, so the URL is a constant. */
static final String SEND_URL = "https://api.iternio.com/1/tlm/send";
/**
* Read-only endpoint used to check credentials. The Test button must never touch
* /tlm/send: a placeholder sample would tell ABRP the car really is in that state and
* corrupt a live route plan.
*/
static final String VERIFY_URL = "https://api.iternio.com/1/tlm/get_next_charge";
private AbrpApi() { }
/** Posts one tlm sample. Throws on a network failure. */
static ServerHttp.Response send(AbrpConfig config, String tlmJson) throws Exception {
byte[] body = formBody(config.apiKey, config.userToken, tlmJson).getBytes(StandardCharsets.UTF_8);
return ServerHttp.send("POST", SEND_URL, null, "application/x-www-form-urlencoded", body);
}
/**
* Checks the credentials without writing anything. This endpoint only takes the token as a
* query parameter; the API key goes in the Authorization header. One token in one URL when
* the user presses Test beats writing fake telemetry on every press.
*/
static ServerHttp.Response verify(AbrpConfig config) throws Exception {
String url = VERIFY_URL + "?token=" + URLEncoder.encode(config.userToken, StandardCharsets.UTF_8.name());
return ServerHttp.send("GET", url, "APIKEY " + config.apiKey, null, null);
}
/** The form body: credentials and the sample, each URL-encoded. */
static String formBody(String apiKey, String userToken, String tlmJson) throws Exception {
String utf8 = StandardCharsets.UTF_8.name();
return "api_key=" + URLEncoder.encode(apiKey, utf8)
+ "&token=" + URLEncoder.encode(userToken, utf8)
+ "&tlm=" + URLEncoder.encode(tlmJson, utf8);
}
/** ABRP answers 200 with {"status":"ok"}; anything else is a rejection. */
static boolean accepted(ServerHttp.Response response) {
if (!response.ok()) return false;
try {
return "ok".equals(new org.json.JSONObject(response.body).optString("status"));
} catch (Exception e) {
return false;
}
}
}