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; } } }