# Nils' MG4 An Android Automotive app for the **MG4 (SAIC eh32)** head unit, plus a small server: the car sends its telemetry to my own server at car.k8s.stinnesbeck.com, which stores it in InfluxDB 2, forwards live data to [A Better Route Planner](https://abetterrouteplanner.com) and feeds the Grafana dashboards. No OBD dongle and no phone in the loop: the app runs on the head unit and reads the car's own APIs. - Repository: https://git.stinnesbeck.com/nils/mg4 - Fork of [fatihdonmezdev/FatihsMG4](https://github.com/fatihdonmezdev/FatihsMG4), itself built on malys' EVABRPUploader and Leon Kernan's `ABRP_Uploader`. - What changed and what is still to do: [`SPECS.md`](SPECS.md). > ⚠️ **No warranty, no liability.** This software is provided "as is" and runs on a > **vehicle**. Installing it is your decision and your risk; see > [`DISCLAIMER.md`](DISCLAIMER.md). Not affiliated with SAIC, MG Motor or Iternio. > MG and MG4 are third-party marks used only to identify compatibility. > **Licence.** The original `ABRP_Uploader` carries no licence, which limits what may legally > be published. Read [`LICENSE.md`](LICENSE.md) before redistributing anything. --- ## Contents - [Screenshot](#screenshot) - [How it fits together](#how-it-fits-together) - [The app](#the-app) - [Install on the car](#install-on-the-car) - [Setup](#setup) - [Building](#building) - [Server and dashboards](#server-and-dashboards) - [Project layout](#project-layout) - [Project documents](#project-documents) - [Credits](#credits) ## Screenshot Version 2.8.2 on the head unit's 1920x720 screen, in full screen (Android 9 emulator; no vehicle there, so every value shows as a dash).

Overview tab: battery, range, range estimates, power and speed tiles above the energy and tyre-pressure cards

## How it fits together ``` car ──live telemetry───▶ car.k8s.stinnesbeck.com (Go) ──▶ ABRP ──daily chunks─────▶ │ ──charging sessions▶ ▼ InfluxDB 2 ◀── Grafana ``` The ABRP credentials live only on the server, never in the APK. The car authenticates with a token of its own, obtained by pairing with a phone (below). ## The app | Tab | What it shows | |---|---| | **Overview** | Battery, range, range estimates at current power and at the 5-minute average, power, speed; odometer, climate setpoint and tyre pressures | | **Consumption** | Daily and period consumption, efficiency | | **Charging** | Charging sessions and curves, cost in euro | | **Upload** | Live telemetry on/off, full screen, voice key, upload cadence | | **Server** | Server URL, pairing by QR code, ingest token, installation ID, config import | | **Wi-Fi** | Joins a network the head unit already knows (e.g. the phone hotspot) | | **Logs** | Upload log and the last steering-wheel key reports | - **Opening the app:** the MG4 launcher does not list third-party apps. **Double-press the voice key** on the steering wheel and the app comes to the front (switch on the Upload tab, on by default). - **Full screen:** hides SAIC's top and bottom bars while the app is open; a swipe from the edge shows them briefly (Upload tab, on by default). - **Live telemetry:** every 15 s while driving or charging, much less often while parked. A value the car cannot read is left out, never sent as zero, so ABRP is never told the battery is empty because a read failed. - **Read-only:** the app never writes a vehicle setting. Signals the MG4 does not publish (battery, cabin and outside temperature, pack energy and capacity) are listed in [`SPECS.md`](SPECS.md#signals-the-car-does-not-provide). ## Install on the car The car build (`NilsMG4-unstable-.apk`) runs with the system UID and is signed with the AOSP test platform key that the MG4 head unit uses. It installs next to the old FatihsMG4 app (package `com.stinnesbeck.nilsmg4.unstable`), not over it. Without adb, through the head unit's hidden file manager: 1. Copy the APK to a USB stick and plug it in. 2. Open **Bluetooth Settings** and select the car name. 3. Long-press the **comma** key until the keyboard settings appear. 4. **Android Keyboard Settings → Languages**, then search for `file` in the box at the top. 5. That opens the Files app: open the USB stick, tap the APK and install. With adb: ```sh 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" ``` > ⚠️ Do this **parked**, and only with an APK you trust. See [DISCLAIMER.md](DISCLAIMER.md). Self-updating (OTA) is switched off: the updater points at an unresolvable host until it is pointed at this repository's releases (see `SPECS.md`). ## Setup 1. Open the app (voice key double press, or "Open" right after installing). 2. On the **Server** tab the URL is already `https://car.k8s.stinnesbeck.com`. 3. Tap **Pair with phone**. Scan the QR code with a phone (or open the address shown and enter the code), and approve with the server's approval password. The car receives its token. 4. On the **Upload** tab, turn live telemetry on. The service starts with the car from then on. ### Config file Instead of typing, put the settings in a text file and tap **Import file** on the Server tab. The head unit has no file picker, so copy the file to ``` Android/data/com.stinnesbeck.nilsmg4.unstable/files/ ``` on the USB stick or the internal storage. The app scans that folder on every mounted volume and takes the first file that parses. Format, one `key = value` per line, `#` for comments; keys left out keep their current value: ``` server_url = https://car.k8s.stinnesbeck.com token = the server's INGEST_TOKEN (not needed after pairing) installation_id = only to carry over history from an install older than 2.9.0 interval_sec = 60 # 15 / 30 / 60 / 120 / 300 boost_low_soc = true low_soc_percent = 20 ``` After a successful import the app deletes the file, since it holds the token; credentials live only in the app's encrypted store. The installation ID is derived from the head unit, so a reinstall finds its history again on its own. `installation_id` is only needed to carry history over from an install made before 2.9.0. A paired token belongs to one installation ID: after changing the ID, pair again. ### Power The app holds **no wake lock** and schedules **no alarms**, so it cannot wake a sleeping head unit and costs nothing while the car is off. ## Building With Nix: ```sh nix develop # JDK 21, Android SDK, Go ./gradlew assembleStableDebug # debug APK for a phone or tablet build-car # car APK, signed with the AOSP test platform key ``` `build-car` writes `build/car/NilsMG4-unstable-.apk` and a `.sha256` next to it, and checks the signing certificate before it finishes. Tests and lint: ```sh ./gradlew testStableDebugUnitTest testUnstableDebugUnitTest lintStableDebug ``` [`mise`](https://mise.jdx.dev) tasks from upstream (`mise run check`, `mise run build`) still work for the plain Gradle builds. ## Server and dashboards - **Server** ([`server/`](server/README.md)): Go, API only. Live telemetry, daily consumption chunks, charging sessions, pairing; forwards live data to ABRP. Image at `git.stinnesbeck.com/nils/mg4-server`. - **Kubernetes** ([`k8s/`](k8s/README.md)): plain manifests for the server, InfluxDB 2 and Grafana, applied by hand with `kubectl apply -k k8s/`. Secrets go in the gitignored `k8s/secrets/secrets.yaml`, from the template next to it. - **Grafana dashboards** (`k8s/grafana/dashboards/`): Live, Consumption and Charging, provisioned with the InfluxDB datasource. ## Project layout ``` app/src/main app: upload service, server client, pairing, UI app/src/stable no-op update hook (no updater in the APK) app/src/unstable car build: system UID, updater (currently pointed nowhere) app/src/debug VHAL probe tools, absent from release builds app/src/test JVM unit tests EVHardware shared vehicle reads per firmware generation server/ Go server k8s/ Kubernetes manifests and Grafana provisioning flake.nix dev shell and build-car ``` ## Project documents | Document | What it covers | |---|---| | [SPECS.md](SPECS.md) | Journal of this fork: architecture, status, to-dos, done log | | [FIRMWARE.md](FIRMWARE.md) | MG4 firmware generations and how the app reads each | | [AGENTS.md](AGENTS.md) | Context for AI agents working in this repository | | [DESIGN.md](DESIGN.md) | The EVSuite design system (the app overrides its palette) | | [SECURITY.md](SECURITY.md) | Threat model | | [DISCLAIMER.md](DISCLAIMER.md) | Vehicle-safety disclaimer; read before installing | | [CHANGELOG.md](CHANGELOG.md) | Upstream release history | | [LICENSE.md](LICENSE.md) | Licence status | ## Credits - **fatihdonmezdev**: [FatihsMG4](https://github.com/fatihdonmezdev/FatihsMG4), the app this fork starts from. - **malys**: EVABRPUploader. - **Leon Kernan**: the original `ABRP_Uploader`, the car-API approach and the first working uploader. - ABRP telemetry API: [Iternio](https://documenter.getpostman.com/view/7396339/SWTK5a8w).