diff --git a/OTA.md b/OTA.md
index 6a0c41f..c6780a4 100644
--- a/OTA.md
+++ b/OTA.md
@@ -10,7 +10,7 @@ build-car ─▶ publish-car ─▶ forge pre-release (private repo, git.stinnes
car server /v1/app/releases, /v1/app/assets/…
│ the car's own paired token
▼
- app: refresh button → check → SHA-256 → install
+ app: Settings → Check for updates → SHA-256 → install
```
The repository stays private and the forge token stays on the server: the car asks its own
@@ -51,7 +51,7 @@ The version comes from the **asset file name**, not the tag.
## Installing
-Press the refresh button in the app's top bar. The app checks, downloads the APK into
+Press **Check for updates** on the Settings tab. The app checks, downloads the APK into
**Downloads** (with progress), verifies its SHA-256, and hands it to a `PackageInstaller`
session. As the system user it may install without asking; if the platform wants a
confirmation, the confirmation screen opens. When a session cannot start at all, the file
diff --git a/README.md b/README.md
index 2c122bd..0d0ab67 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@ APIs.
## Contents
-- [Screenshot](#screenshot)
+- [Screenshots](#screenshots)
- [How it fits together](#how-it-fits-together)
- [The app](#the-app)
- [Install on the car](#install-on-the-car)
@@ -36,15 +36,31 @@ APIs.
- [Project documents](#project-documents)
- [Credits](#credits)
-## Screenshot
+## Screenshots
-Version 2.8.2 at the head unit's 1920x720 pixels and 160 dpi, in full screen (Android 9 emulator; no
-vehicle there, so every value shows as a dash).
+Version 2.18.1 in the car's app window (1778×640 dp next to the MG4's own sidebar), full screen,
+on the Android 9 emulator. There is no vehicle there, so every value shows as a dash. Every tab
+fits the window without scrolling.
-
+
+
+
+  |
+  |
+
+
+  |
+  |
+
+
+  |
+  |
+
+
+
## How it fits together
```
@@ -63,12 +79,12 @@ with the server using a token of its own, obtained by pairing with a phone (belo
| Tab | What it shows |
|---|---|
-| **Overview** | Battery, range, range since start and at the 5-minute average with their usage, energy since start, power and speed with their 5-minute averages; odometer, climate setpoint and tyre pressures |
-| **Consumption** | Daily and period consumption, efficiency |
-| **Charging** | Charging sessions and curves, cost in euro |
+| **Overview** | Battery, range, power and speed; range, usage and energy since start; 5-minute averages of range, usage, power and speed; vehicle info (odometer, climate setpoint) and tyre pressures. Read every half second |
+| **Consumption** | Lifetime, last 7 and 30 days, Trip A and B (distance, energy, average consumption and speed, SOC change), and a daily log of the last four months |
+| **Charging** | The current session (SOC, voltage, current, power, energy into the battery and from the grid, time, cost) and its SOC/power curve |
| **Live data** | Live data on/off, ABRP credentials for sending directly from the car, upload frequency |
| **Server** | Server URL, pairing by QR code, ingest token, installation ID, config import |
-| **Settings** | Full screen, voice key, tyre pressure unit, accent colour, battery (usable capacity, manual SoH), charging price, version, update check, About |
+| **Settings** | Full screen, voice key, tyre pressure unit, accent colour, battery (manual SoH, usable capacity), charging price, entry on the phone, version, update check, install from USB stick, About |
| **Logs** | Upload log |
- **Opening the app:** the MG4 launcher does not list third-party apps. **Double-press the
@@ -107,9 +123,15 @@ 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).
-After that first install, the app updates itself: publish a build with `publish-car`, then
-press the refresh button in the app's top bar (`OTA.md`). The car fetches it through your car
-server, so the repository stays private.
+After that first install, the app updates itself, two ways (details in [`OTA.md`](OTA.md)):
+
+- **Over the air:** publish a build with `publish-car`, then press **Check for updates** on the
+ Settings tab. The car fetches it through your car server, which relays the forge's release, so
+ the car never holds a forge credential.
+- **From a USB stick:** copy `NilsMG4-unstable-.apk` and its `.sha256` from `build/car/`
+ to the stick's top folder and press **Install from USB stick** on the Settings tab.
+
+Either way the app checks the SHA-256, the package and the signature before installing.
## Setup
@@ -122,7 +144,7 @@ server, so the repository stays private.
### On your phone
-Once the car is paired, **Enter on phone** (Live data tab, and Settings → Charging price) shows a
+Once the car is paired, **Enter on phone** (Live data tab, and Settings → On your phone) shows a
QR code. It opens a form on your server where you type the ABRP API key and user token, the
charging price and the usable capacity, and send them with the approval password. The car
collects them within a few seconds and saves them through the same checks as the fields on
@@ -186,13 +208,19 @@ Tests and lint:
./gradlew testStableDebugUnitTest testUnstableDebugUnitTest lintStableDebug
```
+A head-unit emulator for checking layouts and installs: `nix run .#car-emulator` (add
+`-no-window` for headless) starts Android 9 at 1920×720 and 160 dpi. It is signed with the same
+platform key as the car, so the car build installs there as the system user;
+`adb shell wm size 1778x640` gives the car's app window. It has no vehicle, so values stay "—".
+
[`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. Image at
+ chunks, charging sessions, pairing, settings entered on a phone, and the release relay for the
+ in-app updater. 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
@@ -205,13 +233,13 @@ work for the plain Gradle builds.
```
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/unstable car build: system UID, updater (via the car server) and USB install
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
+flake.nix dev shell, build-car, publish-car, car-emulator
```
## Project documents
@@ -219,8 +247,9 @@ flake.nix dev shell and build-car
| Document | What it covers |
|---|---|
| [SPECS.md](SPECS.md) | Journal of this fork: architecture, status, to-dos, done log |
+| [OTA.md](OTA.md) | Publishing releases, the updater's checks, USB install |
| [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 |
+| [AGENTS.md](AGENTS.md) | Overview of the repository for AI agents and new contributors |
| [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 |
diff --git a/SPECS.md b/SPECS.md
index bfc4635..a2c0d45 100644
--- a/SPECS.md
+++ b/SPECS.md
@@ -118,6 +118,10 @@ tyre pressures, GPS (lat, lon, elevation, heading) and the is_* flags. It does *
confirmed car facts, app structure, UI conventions, build/test/ship, documents. It replaces
upstream's EVABRPUploader text. The test emulator is now in the flake: `nix run .#car-emulator`
(Android 9 AOSP at 1920×720, 160 dpi; the car build installs as the system user — checked).
+- 2026-10-18 — README brought up to date: screenshots of all seven tabs from 2.18.1 at the car's
+ 1778×640 window (the upstream and 2.8.2 screenshots are gone), tab descriptions, updating over
+ the air and from a USB stick, the emulator, the release relay. OTA.md: updates are checked from
+ Settings, no longer from a top-bar button.
- 2026-10-18 — App 2.18.1: every tab fits the car's window (1778×640 dp) without scrolling — checked
on the emulator by swiping each page and comparing screenshots. Charging and Consumption get wide
layouts with two columns (values | curve; period totals | daily log and storage note); the curve
diff --git a/screenshots/evabrp-2.2.6-abrp.png b/screenshots/evabrp-2.2.6-abrp.png
deleted file mode 100644
index af8ca57..0000000
Binary files a/screenshots/evabrp-2.2.6-abrp.png and /dev/null differ
diff --git a/screenshots/evabrp-2.2.6-overview.png b/screenshots/evabrp-2.2.6-overview.png
deleted file mode 100644
index ed7feae..0000000
Binary files a/screenshots/evabrp-2.2.6-overview.png and /dev/null differ
diff --git a/screenshots/evabrp1.png b/screenshots/evabrp1.png
deleted file mode 100644
index 5cfbe55..0000000
Binary files a/screenshots/evabrp1.png and /dev/null differ
diff --git a/screenshots/evabrp2.png b/screenshots/evabrp2.png
deleted file mode 100644
index 695fa94..0000000
Binary files a/screenshots/evabrp2.png and /dev/null differ
diff --git a/screenshots/nilsmg4-2.18.1-charging.png b/screenshots/nilsmg4-2.18.1-charging.png
new file mode 100644
index 0000000..c695ae5
Binary files /dev/null and b/screenshots/nilsmg4-2.18.1-charging.png differ
diff --git a/screenshots/nilsmg4-2.18.1-consumption.png b/screenshots/nilsmg4-2.18.1-consumption.png
new file mode 100644
index 0000000..bbf144e
Binary files /dev/null and b/screenshots/nilsmg4-2.18.1-consumption.png differ
diff --git a/screenshots/nilsmg4-2.18.1-live-data.png b/screenshots/nilsmg4-2.18.1-live-data.png
new file mode 100644
index 0000000..9120e2e
Binary files /dev/null and b/screenshots/nilsmg4-2.18.1-live-data.png differ
diff --git a/screenshots/nilsmg4-2.18.1-logs.png b/screenshots/nilsmg4-2.18.1-logs.png
new file mode 100644
index 0000000..5753582
Binary files /dev/null and b/screenshots/nilsmg4-2.18.1-logs.png differ
diff --git a/screenshots/nilsmg4-2.18.1-overview.png b/screenshots/nilsmg4-2.18.1-overview.png
new file mode 100644
index 0000000..f892b97
Binary files /dev/null and b/screenshots/nilsmg4-2.18.1-overview.png differ
diff --git a/screenshots/nilsmg4-2.18.1-server.png b/screenshots/nilsmg4-2.18.1-server.png
new file mode 100644
index 0000000..4007a69
Binary files /dev/null and b/screenshots/nilsmg4-2.18.1-server.png differ
diff --git a/screenshots/nilsmg4-2.18.1-settings.png b/screenshots/nilsmg4-2.18.1-settings.png
new file mode 100644
index 0000000..e9c0f83
Binary files /dev/null and b/screenshots/nilsmg4-2.18.1-settings.png differ
diff --git a/screenshots/nilsmg4-2.8.2-overview.png b/screenshots/nilsmg4-2.8.2-overview.png
deleted file mode 100644
index 1bfb9bd..0000000
Binary files a/screenshots/nilsmg4-2.8.2-overview.png and /dev/null differ