# SPECS — Nils' MG4 Journal for this fork of [fatihdonmezdev/FatihsMG4](https://github.com/fatihdonmezdev/FatihsMG4): what has been changed, and what is still to do. - Our repository: https://git.stinnesbeck.com/nils/mg4 - Upstream: https://github.com/fatihdonmezdev/FatihsMG4 (itself a fork of malys / EVABRPUploader and Leon Kernan / ABRP_Uploader). Credited in the app's About dialog. ## Plan: car server at car.stinnesbeck.com The car sends everything to our own server. The server stores it and forwards live telemetry to ABRP, so the ABRP token lives only on the server, never in the APK. ``` car ──live telemetry───▶ car.stinnesbeck.com ──▶ ABRP api.iternio.com/1/tlm/send ──daily totals─────▶ │ ──charging sessions▶ ▼ InfluxDB 2 ``` ### Server — extend `web/` (Next.js) - **Storage: MongoDB → InfluxDB 2.** Rewrite `web/src/lib/mongo.ts` / `data.ts` against one bucket (tag `installationId` on every point): | Measurement | Timestamp | Tags | Fields | |---|---|---|---| | `telemetry` | sample time | — | soc, power, speed, lat, lon, heading, elevation, is_charging, is_dcfc, is_parked, capacity, soe, odometer, est_battery_range, batt_temp, cabin_temp, ext_temp, hvac_setpoint, voltage, current, tire_pressure_fl/fr/rl/rr | | `consumption_daily` | the day, 00:00 UTC | — | km, kwh, hours, socDrop, sohPercent | | `charging_session` | startedAt | sessionId | endedAt, durationSeconds, energyKwh, gridEnergyKwh, pricePerKwh, totalCost, start/endSocPercent | | `charging_curve` | point time | sessionId | powerKw, socPercent | Same measurement + tags + timestamp overwrites, so the car's retries stay idempotent (MongoDB used unique indexes for that). - **New endpoint `POST /v1/telemetry`:** a batch of live samples, ingest-token auth, validated and bounded like the existing endpoints. - **ABRP forwarder:** after storing, forward the newest sample to `tlm/send` with `ABRP_API_KEY` and `ABRP_USER_TOKEN` from the environment. Rate-limited; a failed forward is logged but not retried, since stale live data is useless to ABRP. Off when the token is unset. - **Existing endpoints stay:** `PUT /v1/consumption/daily`, `PUT /v1/charging-sessions/:id`, `GET /v1/consumption/history/:id`, the dashboard and `/v1/health`. - **Self-hosting:** drop the Vercel assumptions, `output: "standalone"`, a `Dockerfile`, and `ensureIndexes()` replaced by bucket checks at startup. ### Car app - Restore the live telemetry payload and upload cadence from before `ea8032a` (`TelemetryPayload`, `UploadCadence`, location access), sending to the server instead of ABRP. - Server URL and ingest token: open question, see below. - Generate our own `CONSUMPTION_INSTALLATION_ID`. ### Image and deployment - Image: `git.stinnesbeck.com/nils/mg4-server:`, pushed to the forge's registry (needs a registry login on the build machine). - `k8s/` with plain manifests: Namespace, InfluxDB StatefulSet + PVC + Service (cluster-internal), server Deployment + Service + Ingress for `car.stinnesbeck.com` (TLS), and Secret *templates* with placeholder values only. - **Nothing is ever applied to the cluster by us.** Nils applies the manifests. ### Open questions - [ ] Run InfluxDB inside the cluster from our manifests (assumed), or use an existing instance? - [ ] Ingress class and TLS setup (e.g. cert-manager `ClusterIssuer` name), or an existing wildcard certificate Secret? - [ ] Car config: bake the server URL and ingest token into the APK at build time (as upstream does), or enter them in the app, which restores the settings tab with the config-file import? In the app keeps the token out of the APK. - [ ] ABRP API key: the open-source default key from upstream, or our own? ## To do - [ ] **About dialog link and QR code → https://git.stinnesbeck.com/nils/mg4.** Both come from `REPOSITORY_URL` in `app/src/main/java/com/evsuite/abrp/MainActivity.java` and `about_repository` in `app/src/main/res/values/strings.xml`. They still point to Fatih's GitHub repository. - [ ] **Re-enable OTA updates against our own releases.** `RELEASES_API` in `app/src/unstable/java/com/evsuite/abrp/OtaUpdater.java` points at `updates.invalid` on purpose, so checks fail. The updater speaks the GitHub releases API and only allows GitHub hosts (`ALLOWED_HOSTS`). For git.stinnesbeck.com it needs the host on the allowlist, and the response parsing checked against that server's releases API. Release assets must be named `NilsMG4-unstable-.apk` with a `.sha256` sidecar (`build-car` writes both). - [ ] **ABRP upload is gone upstream.** Upstream commit `ea8032a` ("chore", 2026-10-09) removed the ABRP tab, the config-file import (`ConfigImport`), the upload (`AbrpApi`, `TelemetryPayload`, `UploadCadence`), `SecurePrefs` and `HeadUnitWeather`. The app now only tracks consumption and syncs it to the MongoDB API. Leftover `strings.xml` entries (`btn_import`, `import_*`, `hint_api_key`, …) belong to it. Decide whether to restore it. - [ ] **Fix the unit tests.** They don't compile upstream: `AbrpApiTest`, `TelemetryPayloadTest` and others still use `AbrpApi`/`TelemetryPayload`, which upstream commit `ea8032a` deleted. - [ ] **Charging currency.** Prices and costs are shown in Turkish lira (`₺/kWh`, `₺`) in `pane_charging.xml` and `MainActivity.java`. - [ ] **Cloud consumption sync.** `CONSUMPTION_INSTALLATION_ID` in `app/build.gradle` is a fixed UUID inherited from upstream. Set our own before configuring `CONSUMPTION_API_URL` / `CONSUMPTION_API_TOKEN`, so our data cannot land under upstream's id. - [ ] **Translate the rest.** The `web/` dashboard (Next.js) and docs (`CHANGELOG.md`, `OTA.md`'s message table, comments in `mise.toml`/`app/build.gradle`) are still Turkish or French. - [ ] **Docs still use the old package ID.** `README.md`, `OTA.md`, `CHANGELOG.md`, `mise.toml` and `docs/MG4_PLATFORM_INSTALL.md` refer to `com.evsuite.abrp(.unstable)` and `FatihsMG4-*.apk`. - [ ] **Move hardcoded UI strings into `strings.xml`.** Many are inline in Java and layouts. ## Done - 2026-10-17 — Translated the app UI from Turkish to English. - 2026-10-17 — Nix flake: `nix develop` provides JDK 21 and the Android SDK. `build-car` builds the unstable APK and signs it with the AOSP test platform key, which the MG4 head unit uses (cert SHA-256 `c8a2e9bc…192ab8`). - 2026-10-17 — OTA update checks disabled by pointing at an unresolvable host (see To do). - 2026-10-17 — Renamed to "Nils' MG4": package ID `com.stinnesbeck.nilsmg4` (`.unstable` for the car build), APK names `NilsMG4-*.apk`. It installs next to the old FatihsMG4 app instead of replacing it, and starts with empty settings and history. - 2026-10-17 — Temporary on-screen diagnostics (b2a5b32) to debug blank values without adb; removed again (1002962) once values showed on the car. Revert 1002962 to bring them back. ## Installing on the car ``` nix develop build-car adb push build/car/NilsMG4-unstable-.apk /data/local/tmp/update.apk adb shell "pm install -r -f -d -t /data/local/tmp/update.apk" ``` To remove the old upstream app: `adb uninstall com.evsuite.abrp.unstable`.