Files
mg4/SPECS.md
T
nilsandClaude Opus 5.5 af15b15db1 App 2.7.0: voice-key double press opens the app; no scroll jumps
Steering wheel: a double press of the voice key (code 287) opens the
app. The floating button from 2.6.0 does not show over the MG4
launcher, so this is the way back into the app. The head unit reports
keys as the broadcast com.saic.keyevent.hardkey.report, whose extras
are not documented: the receiver tries the usual names (and an
embedded KeyEvent), and counts one press per report whichever way the
up/down field turns out to work; long presses never open the app. The
last 20 reports are listed raw on the Logs tab, so if the guess
misses, a photo of that list gives the real format. A switch in the
Upload tab turns it off. DoublePressDetector (500 ms window) has 5
tests; on the emulator, simulated double presses open the app and
single presses, other keys and long presses do not.

Scrolling: pages jumped back to the top on every 2 s refresh. A stack
trace on the emulator showed why: ViewPager2's RecyclerView lays the
pages out while measuring, at a provisional height, and
ScrollView.onLayout clamps the position to 0. StableScrollView keeps
the driver's position across layouts and is now the root of every
page; the scrolled Charging page stays put across refreshes.

Vehicle tab: all six value tiles, including both range estimates, sit
in one row at the top, so nothing needs scrolling at 1920x720.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
2026-10-17 23:24:48 +02:00

145 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
## Architecture: car server at car.k8s.stinnesbeck.com
The car sends everything to our own server. The server stores it in InfluxDB 2 and forwards live
telemetry to ABRP, so the ABRP token lives only on the server, never in the APK. Grafana reads
InfluxDB directly for the dashboards.
```
car ──live telemetry───▶ car.k8s.stinnesbeck.com (Go) ──▶ ABRP api.iternio.com/1/tlm/send
──daily chunks─────▶ │
──charging sessions▶ ▼
InfluxDB 2 ◀── Grafana (car-grafana.k8s.stinnesbeck.com)
```
- **Server:** `server/`, Go 1.26, API only — see `server/README.md` for endpoints, configuration
and the storage layout. Replaced the Next.js app from upstream (`web/`, removed 2026-10-17).
- **Dashboards:** Grafana, provisioned from `k8s/grafana/`: Live (stats, SoC/range, power/speed,
route map, temperatures, tyres), Consumption (period totals, lifetime, SoH, per-day energy,
distance and efficiency) and Charging (totals, session table, curves). Prices in euro.
- **Pairing:** the car shows a QR code, a phone approves it with the approval password, and the car
gets its own token (RFC 8628-style; `server/README.md`). The shared `INGEST_TOKEN` still works.
- **Car app:** Server tab (URL, pairing by QR, ingest token, installation id, config import); live telemetry
every 15 s while driving or charging; consumption chunks and charging sessions as before.
- **Deployment:** `k8s/`, plain manifests. **Nothing is ever applied to the cluster by us** —
Nils applies them (`k8s/README.md`). TLS comes from Traefik's default certificate.
### Status
- [x] Go server 2.0.0: tests and govulncheck clean; image builds (about 11 MB).
- [x] End-to-end in podman (server + InfluxDB 2.7.12 + Grafana 13.2.3): every endpoint, retry
idempotency, all 25 dashboard queries return data, dashboards rendered and checked.
- [x] Manifests validated offline (including Traefik CRDs). **Not applied.**
- [x] Car app 2.4.0 (pairing by QR): unit tests pass; **not yet tried on the car**.
- [x] Image `mg4-server:2.1.0` (with pairing) pushed to git.stinnesbeck.com (public; pinned by
digest `sha256:e5f6e2d2…` in `k8s/server.yaml`).
- [ ] Check Grafana's host in `k8s/ingress.yaml` (placeholder `car-grafana.k8s.stinnesbeck.com`).
- [ ] First pairing and drive with 2.4.0: telemetry arrives in InfluxDB, Grafana and ABRP.
### Signals the car does not provide
Checked 2026-10-17 against what actually reached InfluxDB (`schema.measurementFieldKeys`). The car
sends soc, est_battery_range, odometer, power, voltage, current, speed, hvac_setpoint, the four
tyre pressures, GPS (lat, lon, elevation, heading) and the is_* flags. It does **not** send:
- **Battery temperature:** read from `EV_BATTERY_AVERAGE_TEMPERATURE`, an Android 14 property; the
head unit runs Android 9, and the SAIC services offer none.
- **Cabin temperature:** `HVAC_TEMPERATURE_CURRENT` is not published; only the setpoint is.
- **Outside temperature:** not published by the vehicle (upstream noted the same). Upstream used
the head unit's weather service as a stand-in (`HeadUnitWeather`, removed in `ea8032a`);
restoring it was considered and declined.
- **Pack energy (`soe`) and capacity:** not published. The Live dashboard computes energy in pack
as SOC × usable capacity, from the hidden dashboard constant `usableKwh` (50.8 kWh for the
51 kWh battery) in `k8s/grafana/dashboards/live.json`.
## 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-<version>.apk` with a `.sha256` sidecar (`build-car` writes both).
- [ ] **Grafana high availability needs another database.** Grafana keeps its own state (users,
sessions, preferences, provisioned-dashboard records, alerting state) in SQLite at
`/var/lib/grafana/grafana.db` on the `grafana-data` PVC. That is fine for one replica, and is
why `k8s/grafana.yaml` uses `replicas: 1` and the `Recreate` strategy: only one Grafana may
write the file. To run more than one replica, first move Grafana to PostgreSQL or MySQL
(`GF_DATABASE_TYPE`, `GF_DATABASE_HOST`, …); the replicas then share that database instead
of a volume. The car data itself is in InfluxDB and unaffected.
- [ ] **Telemetry queue survives restarts.** Unsent live samples are kept in memory only.
- [ ] **Translate the rest.** The 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 — Server rewritten for InfluxDB 2 with live telemetry and ABRP forwarding; Dockerfile;
Kubernetes manifests (not applied).
- 2026-10-17 — App 2.3.0: Server tab (URL, ingest token, installation id, config import), live
telemetry restored from before `ea8032a` and sent to the server, Upload-tab cadence controls
reconnected, dead switches removed. The removed ABRP classes' unit tests compile and pass again
(AbrpApi and HeadUnitWeather tests deleted with their classes).
- 2026-10-17 — Accent colour: MG red instead of muted gold. Web app renamed to Nils' MG4.
- 2026-10-17 — Dashboard translated to English (en-GB formatting); euro instead of lira in the
dashboard and the car app's charging tab.
- 2026-10-17 — Server rewritten in Go (API only); dashboards moved to Grafana with provisioned
InfluxDB datasource and three dashboards; the Next.js app (`web/`) removed. Per-host
cert-manager certificates dropped in favour of Traefik's default certificate.
- 2026-10-17 — Pairing by QR code: server 2.1.0 issues per-car tokens after approval on a phone;
app 2.4.0 shows the QR code and collects its token. Tested end to end against InfluxDB.
- 2026-10-17 — Incident: a commit swept the filled-in `secrets.example.yaml` (InfluxDB admin token,
INGEST_TOKEN) into git and pushed it. Commit amended and force-pushed; both values regenerated in
the now gitignored `k8s/secrets.yaml` (since moved to `k8s/secrets/secrets.yaml`, outside
`kubectl apply -f k8s/`). Filled-in secrets live only there from now on.
- 2026-10-17 — App 2.7.0: a double press of the steering wheel's voice key (code 287) opens the
app, from the head unit's `com.saic.keyevent.hardkey.report` broadcast. Its extras are not
documented; the usual names are tried and every report is listed raw on the Logs tab. The
floating button (2.6.0) did not show over the MG4 launcher. Pages no longer jump back to the
top on every refresh (`StableScrollView`), and the Vehicle tab shows all six value tiles,
including both range estimates, in one row at the top.
- 2026-10-17 — App 2.6.0: floating button to open the app, because the MG4 launcher does not list
it. Drawn over other apps (needs the overlay permission, which the car build has through the
system UID), draggable, hidden while the app is open, switch in the Upload tab. Tested on an
Android 9 AOSP emulator with the car APK (installs as UID 1000 there too), at 1920x1080 and at the
head unit's real 1920x720 / 160 dpi, where all tabs and the button were checked as well.
- 2026-10-17 — App 2.5.0: range estimates at current and 5-minute average power (Vehicle tab).
- 2026-10-17 — Live dashboard reworked around the signals the car sends: Climate setpoint, Last
update and a computed Energy in pack replace the empty temperature tiles; Battery voltage and
current replaces the Temperatures chart.
- 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-<version>.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`.