Settings → Display now has the Overview style first with the accent colour beside it; for styles with their own palette the accent field is greyed out with a note. Styles are an enum, OverviewStyle, with usesAccent (unit-tested). Emulator only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
576 lines
47 KiB
Markdown
576 lines
47 KiB
Markdown
# 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, which stores it in InfluxDB 2, and its live data
|
||
straight to ABRP with the ABRP credentials kept (encrypted) in the app. The server does not talk
|
||
to ABRP. Grafana reads InfluxDB directly for the dashboards.
|
||
|
||
```
|
||
car ──live telemetry───▶ car.k8s.stinnesbeck.com (Go)
|
||
│ ──daily chunks─────▶ │
|
||
│ ──charging sessions▶ ▼
|
||
│ InfluxDB 2 ◀── Grafana (car-grafana.k8s.stinnesbeck.com)
|
||
└──live telemetry─────▶ ABRP api.iternio.com/1/tlm/send
|
||
```
|
||
|
||
- **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/`, reading with a read-only token: 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, bound to its installation id (RFC 8628-style; `server/README.md`). The
|
||
shared `INGEST_TOKEN` still works. The same QR flow carries settings typed on the phone.
|
||
- **Car app:** tabs Overview, Consumption, Charging, Live data (on/off, ABRP credentials,
|
||
frequency), Server (URL, pairing, token, installation id, config import), Settings (display,
|
||
voice key, tyre unit, accent colour, battery, charging price, Wi-Fi), Logs. Live telemetry every
|
||
15 s while driving or charging, to the server and straight to ABRP; consumption chunks and
|
||
charging sessions to the server.
|
||
- **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
|
||
|
||
Current: app 2.14.2, server 2.5.0 (pinned by digest in `k8s/server.yaml`).
|
||
|
||
- [x] On the car: QR pairing, values on the Overview, voice-key double press, full screen
|
||
without black bands.
|
||
- [x] Go server: tests and govulncheck clean; end to end in podman against InfluxDB 2.7.12
|
||
(telemetry, consumption, charging, pairing, settings from the phone) and Grafana 13.2.3 (all
|
||
dashboard queries return data).
|
||
- [x] Manifests validated offline (kubeconform, with Traefik CRDs). **Applied by Nils only.**
|
||
- [ ] Check Grafana's host in `k8s/ingress.yaml` (placeholder `car-grafana.k8s.stinnesbeck.com`).
|
||
- [x] Screen on the car: 1920×720 px at 160 dpi, app window 1778×720 px (Logs → SCREEN).
|
||
- [x] Car-colour probe on the car: only `ro.build.mtk2712.vehicle_type = AS28`; the paint colour
|
||
is not in the system properties. It may be in the VHAL (a debug-build VhalProbe listing
|
||
would tell); until then the accent is chosen by hand.
|
||
- [ ] Not yet tried on the car: live data straight to ABRP (2.11), Enter on phone (2.12), accent
|
||
colours (2.14), right-aligned values (2.14.1).
|
||
|
||
### 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
|
||
|
||
- [ ] **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.
|
||
- [ ] **EVHardware's comments are partly French.** About 120 comment lines in the vendored
|
||
`EVHardware/` library (mostly `EVHardware.kt`, some tests), half-translated upstream. The
|
||
app, the docs and the build files are English.
|
||
- [ ] **Idea: climate energy on the Overview (motor vs climate).** Nothing the app reads splits
|
||
energy by consumer: battery power (voltage × current) is the total of motor, climate, 12 V
|
||
and the rest. SAIC's charging service, which EVHardware already binds, has counters that
|
||
might: `TotalConsumption`, `TotalRegen` and `AccConsumption`, each since start and since the
|
||
last charge (`SaicCharging.consumedKwhSinceStart()`, `regeneratedKwhSinceStart()`,
|
||
`accCounterKwhSinceStart()` and the `…SinceCharge()` twins). Whether "Acc" means
|
||
*accessory* (a measured climate/auxiliary share; motor ≈ total − acc) or *accumulated* (a
|
||
duplicate of the total) is unknown — EVHardware deliberately does not call it climate — and
|
||
whether SWI68 answers at all is untested. Plan: (1) a read-only diagnostic showing the raw
|
||
counters since start; (2) two drives, climate off and on, with a photo of the counters
|
||
after each; (3) if Acc grows only with the climate, an Overview card "Climate &
|
||
accessories since start" with the motor's share beside it — otherwise drop the diagnostic
|
||
and, at most, show a labelled estimate (power drawn at a standstill with the climate on).
|
||
|
||
## 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-18 — `english-translation` merged into the default branch (merge commit `f54e684`, the
|
||
summary as its message), `master` renamed to `main`, the work branch deleted. Work goes on `main`.
|
||
- 2026-10-18 — AGENTS.md rewritten as the entry point for agents: what the project is, rules,
|
||
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-24 — App 2.27.1 (branch `styles-retro-future`): Settings → Display rearranged:
|
||
Overview style first, the accent colour beside it, tyre pressure on its own row below. The
|
||
accent colour is only offered for styles drawn in it (Tiles, Gauges, Cockpit, Retro (accent
|
||
colour), Futuristic, MG, Minimal, Chronograph); for the styles with their own palette (Retro,
|
||
Sci-fi, Cartoon, Blueprint, Terminal, Aviation, Nixie tubes) the field is greyed out and
|
||
ignores taps, keeping the colour chosen before, and a line says "… has its own colours. To
|
||
change the accent colour, pick a style that uses it." The styles are now an enum,
|
||
`OverviewStyle` (stored names, `usesAccent`, the old `DASHBOARD` value; unit-tested), in place
|
||
of the string array. The style list is wider than its field so "Retro (accent colour)" fits.
|
||
Checked on the emulator: Tiles (accent open), Cartoon (greyed, tap ignored, note), back to
|
||
Tiles; the page fits without scrolling
|
||
(`screenshots/nilsmg4-2.27.1-settings-own-colours.png`). Not tried on the car.
|
||
- 2026-10-24 — App 2.27.0 (branch `styles-retro-future`): the Overview can go full width. A
|
||
small round button in its top-right corner (40 dp disc in a 72 dp touch area, over the pages)
|
||
hides the app's navigation (sidebar, or the bottom bar on narrow screens); its icon turns to
|
||
"shrink" and a second tap brings the navigation back. Only on the Overview: other pages
|
||
always show the navigation and no button, swiping between pages still works, and coming back
|
||
to the Overview restores full width. Remembered (`overview_fullscreen`). The top-right corner
|
||
was chosen after looking at all fifteen styles (top left covered Tiles' "Battery"); Minimal's
|
||
clock moved left of it. Checked on the emulator: toggle, swipe away and back, restart, Tiles
|
||
and MG at full width (`screenshots/nilsmg4-2.27.0-overview-fullscreen.png`); no crash. Not
|
||
tried on the car.
|
||
- 2026-10-24 — App 2.26.0 (branch `styles-retro-future`): five more Overview styles, chosen to
|
||
differ in arrangement as much as in look, on a new base class `ClusterCanvasView` (values,
|
||
easing, null-safe formatting, animating only while something moves and the view is shown).
|
||
**Minimal** (`MinimalView`): the speed alone, very large and thin; power as a short line
|
||
growing left (green, regenerating) or right from the centre; the battery as a hairline along
|
||
the bottom edge. **Terminal** (`TerminalView`): text only, green monospace with scan lines —
|
||
rows with ASCII bars, the gear as [D], tyres, a prompt with a cursor that blinks as readings
|
||
redraw it; a low battery row turns amber. **Aviation** (`AviationView`): a primary flight
|
||
display — speed and battery on scrolling tapes with readout boxes (blank, not centred on 0,
|
||
when unread), a horizon that pitches with power (up drawing, down regenerating) with a kW
|
||
ladder and the aircraft symbol, annunciators on top, data boxes below. **Nixie tubes**
|
||
(`NixieView`): orange glowing digits in glass tubes with the unlit numerals behind and a mesh,
|
||
brass name plates on walnut; speed, gear and battery large, power, range, usage and clock
|
||
smaller (`SevenSegment` lays the text out in tubes). **Chronograph** (`ChronographView`): a
|
||
watch face — the speed on the main hand over a bezel scale, sub-dials for battery, power and
|
||
usage, the gear in a window, engraved columns either side (its radial gradient is a Canvas
|
||
shader made in `onSizeChanged`). Checked on the emulator with a throwaway build cycling the
|
||
five styles with sample values (drawing and regenerating;
|
||
`screenshots/nilsmg4-2.26.0-minimal.png`, `-terminal`, `-aviation`, `-nixie`, `-chronograph`)
|
||
and without data; no crash. Not tried on the car.
|
||
- 2026-10-24 — App 2.25.0 (branch `styles-retro-future`): Overview style "MG" (`OctagonView`),
|
||
after the classic MG dashboards whose dials sat in octagonal bezels — not a copy of the MG4's
|
||
own driver display, and without MG's octagon logo. Three octagonal dials with chrome bezels,
|
||
cream scales and needles in the accent colour: speed in the middle (digits and P R N D under
|
||
the hub), battery E…F on the left (red zone below 20 %, range), power −50…150 kW on the right
|
||
(green charge zone below zero). A cream line below: odometer, trip, 5-minute usage, climate,
|
||
clock, separated by small accent octagons. Needles ease and redraw only until settled, only
|
||
on screen; unread values draw no needle and "—". Palette `mg_*`. Checked on the emulator with
|
||
sample values (`screenshots/nilsmg4-2.25.0-mg.png`) and without data; no crash. Not tried on
|
||
the car.
|
||
- 2026-10-24 — App 2.24.1 (branch `styles-retro-future`): the line of text above every page
|
||
("Your drive at a glance.", "Settings. Display, battery…" and so on) is gone, at the owner's
|
||
request; the pages start at the top, level with the sidebar, and gain about 45 dp. Its 14
|
||
strings and the title arrays are removed. Checked on the emulator: no crash, and every tab
|
||
still fits 1778×640 without scrolling. Not tried on the car.
|
||
- 2026-10-24 — App 2.24.0 (branch `styles-retro-future`): three more Overview styles, each one
|
||
Canvas view fed the shared `ClusterValues` (now with the tyre pressures, formatted in the
|
||
chosen unit). **Sci-fi** (`SciFiView`): a starship console — rounded elbow frames in orange
|
||
and lilac, pill labels, condensed capitals; velocity large, power as blocks (regeneration
|
||
blue), rows for reserves, range, efficiency, since start, trip and climate, the reserves as a
|
||
stack of blocks, gears as blocks in the frame; below 20 % the frames turn red ("red alert").
|
||
**Cartoon** (`CartoonView`): comic panels with ink outlines and hard shadows on night blue; a
|
||
battery with a face (happy, unsure, worried and sweating), a speedometer whose needle springs
|
||
into place (animated only until it settles, only on screen), the speed in a speech bubble,
|
||
the gear on a sticker, a "zap-o-meter" with a sound word (PSST, PURR, VROOM, ZOOM!,
|
||
RECHARGE!), stats on pills. **Blueprint** (`BlueprintView`): a top-view drawing of the car on a
|
||
gridded navy sheet — the battery hatched as far as it is charged, leader lines to each tyre's
|
||
pressure, power as a force arrow (dashed and reversed while regenerating), range and trip as
|
||
dimension lines, the cabin setpoint as a note, and a title block with speed, power, gear,
|
||
usage, odometer and time. Palettes `scifi_*`, `cartoon_*`, `blueprint_*` in colors.xml.
|
||
`MainActivity` now maps each style to its pane in a table (`stylePanes`); the new panes are
|
||
built in code (`drawnPane`). Checked on the emulator with sample values in a throwaway build
|
||
(`screenshots/nilsmg4-2.24.0-scifi.png`, `-cartoon.png`, `-blueprint.png`) and without data
|
||
through the real setting; no crash. Not tried on the car.
|
||
- 2026-10-24 — App 2.23.1 (branch `styles-retro-future`): "Retro (accent colour)", the Retro
|
||
style in the chosen paint colour — display in `evAccent`, labels in `evAccentLight`;
|
||
regeneration stays green, a low battery turns amber (not red, so it never looks like Dynamic
|
||
Red). Same `RetroView`, `setAccentPalette`; listed under Retro, stored as `RETRO_ACCENT`.
|
||
Checked on the emulator in Dynamic Red with sample values
|
||
(`screenshots/nilsmg4-2.23.1-retro-accent.png`). Not tried on the car.
|
||
- 2026-10-24 — App 2.23.0 (branch `styles-retro-future`): two more Overview styles, each one
|
||
Canvas view fed a `ClusterValues` per reading. **Retro** (`RetroView`): an 80s digital cluster
|
||
as a vacuum fluorescent display — slanted seven-segment digits (`SevenSegment`, unit-tested)
|
||
with the unlit segments faintly visible, amber labels, a lit P R N D selector, the speed large,
|
||
power as a −50…150 kW bar (regeneration green), charge as a vertical bar (red below 20 %),
|
||
range, 5-minute and since-start usage, trip, climate, clock and odometer; its fixed palette is
|
||
`retro_*` in colors.xml (a style's own colours, not the accent). **Futuristic** (`FlowView`): a
|
||
hairline HUD in the accent colour — a battery capsule of scan lines, an energy channel whose
|
||
particles flow to the wheel while drawing and back in green while regenerating (faster with
|
||
more power), the speed inside a ring of dashes that turns with the speed and a power arc,
|
||
P R N D in a column, and a readout line. It animates at about 30 frames a second only while
|
||
power or speed is non-zero and the view is shown. Unread values show dashes and light or move
|
||
nothing in both. The Overview now reads the car every 0.2 s instead of 0.5 s (a read still
|
||
running skips the tick, as before). Checked on the emulator with sample values in a throwaway
|
||
build (`screenshots/nilsmg4-2.23.0-retro.png`, `…-futuristic.png`) and without data through
|
||
the real setting; no crash. Not tried on the car, including the 0.2 s reading rate.
|
||
- 2026-10-24 — App 2.22.3: the Overview style "Dashboard" is now called "Gauges" (stored as
|
||
`GAUGES`; the old value `DASHBOARD` still selects it, so the choice survives the update;
|
||
`pane_dashboard.xml` is `pane_gauges.xml`). The branches `dashboard-gauges` and
|
||
`dashboard-cockpit`, merged into main, are deleted. Tests and lint.
|
||
- 2026-10-24 — App 2.22.2 (branch `dashboard-cockpit`): the gear badge names all four gears.
|
||
The owner shifted through the lever with 2.22.1 and read the codes off the Logs tab: 1 P,
|
||
2 R, 3 N, 4 D (firmware SWI68-29958-1300R69). `Gear.label` maps them (unit-tested); any
|
||
other code shows no letter, unread "—". The gear list on the Logs tab, which existed only for
|
||
this, is gone again. Codes recorded in AGENTS.md and EVHardware's comments. Unit tests only;
|
||
the mapping itself comes from the car.
|
||
- 2026-10-24 — App 2.22.1 (branch `dashboard-cockpit`): the gear badge on the Dashboard and
|
||
Cockpit no longer guesses. It showed "D" for anything not in P, so N and R read as D. The app
|
||
only knew "in P or not"; EVHardware now passes on the car's raw gear code
|
||
(`EVHardware.getGearRaw`, `EnergySnapshot.gearRaw`; `isVehicleInPark` is built on it).
|
||
`Gear.label`: 1 → "P" (the only established code), any other code → no letter, unread → "—".
|
||
To map R, N and D, the Logs tab lists every change of the code with time and speed
|
||
(`GearLog`, last 12, kept in preferences). Checked on the emulator with a throwaway build
|
||
cycling codes 1–4 (pill shows P on 1, nothing on 2–4; the Logs tab lists each change). Not
|
||
tried on the car; the codes for R, N and D are still to be read there.
|
||
- 2026-10-24 — App 2.22.0 (branch `dashboard-cockpit`, from `dashboard-gauges`): a third Overview
|
||
style, "Cockpit", designed without a reference: three segmented rings (`RingView`, Canvas) —
|
||
battery (0…100 %, range inside, below 20 % in the warning colour), power with the speed and the
|
||
gear inside (zero at the top: drawing power clockwise to 150 kW in the accent colour,
|
||
regeneration anticlockwise to −50 kW in green) and 5-minute usage (0…30 kWh/100 km, a white tick
|
||
for the average since start). Segments brighten towards the leading one. Below, the power of the
|
||
last two minutes as a trace (`TraceView`; `PowerTrace`, one slot per second, unit-tested; gaps
|
||
for unread or missed readings, never zeros), the clock, climate setpoint and odometer. Settings
|
||
→ Overview style: Tiles / Dashboard / Cockpit. Checked on the emulator with sample values in a
|
||
throwaway build (`screenshots/nilsmg4-2.22.0-cockpit.png`) and without data through the real
|
||
setting (nothing lit, "—", the page fits 1778×640). Not tried on the car.
|
||
- 2026-10-24 — App 2.21.0 (branch `dashboard-gauges`): Overview style "Dashboard", an instrument
|
||
cluster after a reference picture of the owner's: power (−50…150 kW), speed (0…180 km/h, with
|
||
TOTAL odometer and TRIP km since start) and battery (E…F, below 20 % in the warning colour,
|
||
range underneath) as gauges (`GaugeView`, Canvas, accent-coloured arcs, white needles that ease
|
||
to a new value), and a strip of gear (P/D), climate setpoint, clock, 5-minute usage and a
|
||
battery bar (`BatteryBarView`). Unread values draw no needle and "—". Settings → Display →
|
||
"Overview style: Tiles / Dashboard"; Tiles stays the default; both are fed from the same
|
||
reading. Checked on the emulator with sample values in a throwaway build
|
||
(`screenshots/nilsmg4-2.21.0-dashboard.png`) and without data through the real setting; the
|
||
Overview and Settings fit 1778×640. Not tried on the car.
|
||
- 2026-10-23 — App 2.20.0: the Overview's Vehicle info card shows the lease mileage: "Lease
|
||
allowance today" (40,000 km over four years from 2023-05-01, accruing evenly per day, in
|
||
`LeaseAllowance`, unit-tested) and "Versus allowance", the odometer against it ("… km over" /
|
||
"… km under", "—" when the odometer is unread). The odometer now has a thousands separator,
|
||
like the new rows. Checked on the emulator with a sample odometer of 25,000 km in a throwaway
|
||
build (34,798 km allowed on 2026-10-23, 9,798 km under); the Overview still fits.
|
||
- 2026-10-23 — App 2.19.9: the Foreground apps card is gone from the Logs tab (it found CarPlay's
|
||
package; nothing left to find), with ForegroundProbe and ForegroundHistory. The Logs tab is one
|
||
card again — update log, then uploads — with the other tabs' page padding.
|
||
- 2026-10-23 — App 2.19.8: the charging curve's labels no longer overlap: "kW" has a row of its
|
||
own above the plot, the power values are right-aligned against the axis, and the SOC ticks read
|
||
"0 %" … "100 %" (the separate "SOC %" sat on top of "100"). The wide Charging tab splits 35/65,
|
||
values | curve. Checked on the emulator with sample AC values and a curve in a throwaway build;
|
||
the page still fits 1778×640.
|
||
- 2026-10-23 — App 2.19.7: AC charging is metered too, while the app runs (owner's choice; Fatih
|
||
had metered DC only, `e9bfa12`, because an overnight AC charge is slept through). A session runs
|
||
whenever power flows into the pack (`ChargingState.isMeteredCharging`: inbound > 0.3 kW and a
|
||
charge by `isCharging`), and its curve takes every inbound sample. Gaps where the head unit
|
||
slept are dropped, not billed, so an overnight AC session shows the awake part only. The label
|
||
says "AC charging" / "DC fast charging"; `isDcCharging` stays for it and ABRP's `is_dcfc`. Server
|
||
comment, server README and the Grafana charging dashboard's texts no longer say DC only (the
|
||
dashboard ConfigMap changes with them: re-apply `k8s/`). Unit tests; not tried on the car.
|
||
- 2026-10-23 — App 2.19.6: the CarPlay full-screen switch and the "Export CarPlay data" button are
|
||
removed (CarPlay's size is fixed, see below). On start, the car build takes the CarPlay entry out
|
||
of `policy_control` if the switch was left on, and leaves anything else there alone — checked on
|
||
the emulator. The Foreground apps card stays as a diagnostic. AGENTS.md lists the CarPlay and
|
||
install findings among the confirmed car facts.
|
||
- 2026-10-23 — CarPlay full screen: not possible on this firmware (SWI68). The export from the
|
||
car showed CarPlay's window at 1920×720 with both SAIC bars hidden (policy_control on), and its
|
||
video SurfaceView at (0,80), 1920×640, with a 1920×640 buffer: the grey strip is the window's own
|
||
background above the video. Allgo's `/system/app/AllgoCarplay_EH32` hard-codes it twice:
|
||
`CarPlayService` announces `SCREEN_HEIGHT = 640`, `SCREEN_WIDTH = 1920` to the iPhone (it reads
|
||
`sys.screen.heightpixel/widthpixel`, then overwrites them with the constants), and
|
||
`carplay_screen_layout.xml` places the SurfaceView at `640px` high with `80px` top margin. Both
|
||
are in the read-only, signed system image. The export (`dump/`) is gitignored: it holds Allgo's
|
||
software.
|
||
- 2026-10-23 — App 2.19.5: installing from USB (and over the air) works on the car again, with a
|
||
confirmation. The update log showed the car refusing the silent session: `Not allowed to install
|
||
non-system apps on internal storage`. The fallback, Android's installer screen, then said "There
|
||
was a problem parsing the package": as the system user, our FileProvider's read grant to the
|
||
installer is refused (`not exported from UID 1000`). The car build now hands the installer a
|
||
file URI to the verified copy in Downloads (StrictMode's file-URI check lifted for that one
|
||
call). On the emulator, with the session forced to fail as on the car, the installer screen
|
||
asked to update and installed it. Not tried on the car yet. OTA.md updated.
|
||
- 2026-10-23 — App 2.19.4: "Export CarPlay data" on the Logs tab (Foreground apps card), for the
|
||
question whether CarPlay's resolution can change. CarPlay with policy_control still leaves a grey
|
||
strip, also after reconnecting: Allgo announces a smaller picture to the iPhone. 20 s after the
|
||
press (time to open CarPlay) it writes to `<stick>/Android/data/<package>/files/
|
||
nilsmg4-carplay-<time>/` — the only place on a removable volume Android 9 lets an app write:
|
||
`dumpsys window windows`, `SurfaceFlinger`, `display`, `activity activities`, `wm size/density`,
|
||
the display-related system properties (identifiers filtered out), Allgo's APK and native
|
||
libraries, and CarPlay/Allgo/iAP2-named files under /system, /vendor, /odm, /product. It reads
|
||
the car and writes only to the stick. Owner's choice to export Allgo's software for analysis
|
||
(2026-10-23). On the emulator (no Allgo there) every dump was written, without permission
|
||
errors. XDA (leonkernan, page 30) found SAIC's SystemUI hides its top bar itself for a whitelist
|
||
that includes com.allgo.carplay.service — compiled into SystemUI, not changeable from the app.
|
||
- 2026-10-23 — App 2.19.3: an update log on the Logs tab, above the uploads. A USB install on the
|
||
car ended in an installer message about a broken package, while the same APK installed through
|
||
the Bluetooth-settings route; the message was only a toast and logcat, so its exact words are
|
||
lost. Now every step is kept (last 10, survives the restart an install causes): file found with
|
||
its size, SHA-256 match, the package manager refusing to parse, a failed installer session with
|
||
its exception, bytes handed to the installer, the platform's result in its own words, and "Now
|
||
running <version>" on the first start of a new version. On the emulator, with a FAT image as a
|
||
removable volume (like the car's stick), the USB install worked silently and the log showed the
|
||
whole sequence; a truncated APK with a matching hash was refused at the package check, logged.
|
||
The car's failure is not reproduced or explained yet.
|
||
- 2026-10-23 — App 2.19.2: Settings → "Full screen for CarPlay (experiment)". The Logs tab showed
|
||
CarPlay on the car as `com.allgo.carplay.service/.CarPlayActivity`, an ordinary activity. The
|
||
switch adds that package to `immersive.full` in Android 9's `policy_control` global setting
|
||
(system user only; elsewhere it is disabled) and, off, removes just that entry; it shows what the
|
||
setting holds. On the emulator: policy_control hid the status bar of another app and its content
|
||
took the space; the switch wrote and removed its entry and left an existing one alone. Not tried
|
||
on the car: whether SAIC's top bar is the status bar, and whether CarPlay's picture grows or
|
||
leaves a black strip until the phone reconnects. Tyre pressure and accent colour now sit side by
|
||
side in the wide Settings layout, so the page still fits.
|
||
- 2026-10-23 — App 2.19.1: "Foreground apps" card on the Logs tab, step 1 of the CarPlay
|
||
full-screen experiment. It reads which apps came to the front in the last 30 minutes (usage
|
||
events, open to the system user without a declared permission), the open tasks, the
|
||
`policy_control` global setting and the status bar height — on opening the tab, on returning to
|
||
it and on Refresh, never in the background. It changes nothing. On the emulator (car build,
|
||
system user) it named Settings, the clock, the calculator and the launcher with times, and the
|
||
page still fits 1778×640 with every line filled. The stable build says it cannot read them. Not
|
||
tried on the car; the CarPlay package name is what this is for. Next: a switch that sets
|
||
`policy_control` to `immersive.full=<that package>`, on approval.
|
||
- 2026-10-19 — `k8s/agent-access/`: a service account `mg4-agent` for a coding agent, limited to
|
||
the `mg4` namespace. Read-only role by default; an optional deploy role. Neither may touch
|
||
Secrets, exec, port-forward or RBAC; the deploy role cannot delete PVCs. `make-kubeconfig.sh`
|
||
writes a kubeconfig with a `kubectl create token` token that expires (default 24 h). Validated
|
||
with kubeconform and shellcheck, the script against a stub kubectl; not applied.
|
||
- 2026-10-19 — App 2.19.0: EVHardware updated from malys/EVHardware `b595ccb` (2026-09-17, the
|
||
base Fatih copied) to `e4e1158` (2026-10-18, after 1.18.0), 42 commits, merged three ways so Fatih's
|
||
changes stay (listed in `LICENSE.md`). Upstream brings among others: trip, charge and eco
|
||
telemetry fixes, a daily battery digest, the snapshot's widest window and raw drive mode and
|
||
regen, probes for the vendor charge status, plug and energy ids, read-only vendor-surface probes,
|
||
exterior-light conditions and actions, and a diagnostic inbox. The app uses only
|
||
`EnergyTelemetryReader` and its snapshot, so the new reads (windows, drive mode, regen) are the
|
||
only new calls it makes; they are reads. The new actions and the inbox are not called. Conflicts
|
||
were only where both sides added fields; the charge-status id is now upstream's
|
||
`PROP_BMS_CHRG_STS`. EVHardware's 491 tests and the app's pass; the app ran on the emulator
|
||
without errors. Not tried on the car.
|
||
- 2026-10-19 — App 2.18.3: the Battery tile's value is 40 sp too. The first row of tiles now
|
||
shares one height, since the Battery tile, with its charging line, grew taller than the rest.
|
||
Checked on the emulator with sample values: the Overview still fits 1778×640. Not tried on the car.
|
||
- 2026-10-19 — App 2.18.2: Overview's Range, Power and Speed tiles (the ones without a line under
|
||
the value) show it at 40 sp instead of 28; auto-size still shrinks a long value. Checked on the
|
||
emulator with sample values ("-142.5 kW", "160 km/h") in a throwaway build: they fit at full
|
||
size and the row keeps its height. Not tried on the car.
|
||
- 2026-10-19 — Server 2.6.1: the code field on the phone pages (`/pair`, `/settings`, when opened
|
||
without the code in the link) upper-cases what is typed and puts the dash in after the third
|
||
character. That takes the pages' first script: one inline script, allowed by its SHA-256 hash in
|
||
the CSP (`script-src 'sha256-…'`, still `default-src 'none'`); a test checks the served script
|
||
against the header. Without it the server still accepts the code in any case, with or without
|
||
the dash. Image pinned by digest in `k8s/server.yaml` (not applied).
|
||
- 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
|
||
table scrolls inside its card, so a long session no longer stretches the page. Live data: ABRP key
|
||
and token side by side, Save / Test / Enter on phone in one row. Settings: save buttons beside
|
||
their fields, charging price under Battery, "Enter on phone" in a card of its own above App. Wide
|
||
pages use 12 dp top padding. With real data the value texts are longer; not tried on the car.
|
||
- 2026-10-18 — App 2.18.0: Settings → App → "Install from USB stick" (car build only): the newest
|
||
`NilsMG4-unstable-<version>.apk` newer than the installed one in a stick's top folder, checked
|
||
against its `.sha256` and the package, installed with the OTA installer session. Tested on the
|
||
emulator: 2.18.0 installed a 2.18.1 from the storage's top folder silently, as the system user —
|
||
so the OTA install step should not need a confirmation either.
|
||
- 2026-10-18 — App 2.17.5: Server tab fits the car's window. The "Your private connection" card is
|
||
gone (its text was out of date); Import from file sits with Save and Test in one row; the
|
||
installation ID is shown as text, not edited, since it is derived from the head unit (a config
|
||
file can still carry another one over). The subtitle no longer says the server forwards to ABRP.
|
||
- 2026-10-18 — App 2.17.3: the Overview panel "Energy and temperature" is "Vehicle info" (2.17.4; "Odometer and climate" in 2.17.3):
|
||
it only holds those two, since the car publishes no pack energy or temperatures.
|
||
- 2026-10-18 — App 2.17.2: Wi-Fi auto-connect removed with its two permissions (ACCESS_ and
|
||
CHANGE_WIFI_STATE). Upstream used it to keep the head unit on a phone hotspot; this car's head
|
||
unit has its own connection.
|
||
- 2026-10-18 — App 2.17.1: Settings without the Wi-Fi and saved-networks cards, in three columns
|
||
(display and steering wheel | battery | charging price and app). Logs keeps only the upload log: the steering-key
|
||
reports, the car-colour probe and the screen line are gone, their questions answered.
|
||
- 2026-10-18 — App 2.17.0: "Speed, 5-min average" card under Speed. The top bar is one compact line
|
||
(title and subtitle); version, update check and About moved to an App card on the Settings tab.
|
||
The Overview fits the car's window without scrolling: checked on the emulator at 1778×640, the
|
||
layout height the car reports (less page padding, a lower tyre card).
|
||
- 2026-10-18 — App 2.16.4: drop-downs again, as Material exposed dropdown menus: their list leaves
|
||
the focus with the activity, so full screen stays (a Spinner's list is a focusable window of its
|
||
own without the full-screen flags). 2.16.3 had replaced them with buttons.
|
||
- 2026-10-18 — App 2.16.3: full screen stays while choosing or reading a dialog. Drop-downs (upload
|
||
frequency, tyre unit, accent colour) became buttons on the page: a drop-down is a window of its
|
||
own without the full-screen flags, so the car showed its bars while it was open. Dialogs are
|
||
shown unfocusable, given the activity's full-screen flags, then allowed focus
|
||
(`showKeepingFullScreen`); none of them takes typing.
|
||
- 2026-10-18 — From the car (2.16.2): screen 1920×720 px at 160 dpi with a 1778×720 px app
|
||
window, so EVProfile's 1280×480 does not apply; colour probe found only `vehicle_type = AS28`.
|
||
FIRMWARE.md, mise.toml and SPECS.md say so.
|
||
- 2026-10-18 — App 2.16.2: Logs → SCREEN shows the panel's pixels and density and the app
|
||
window in px and dp. The MG4's display figures disagree (1920×720 px used here; EVProfile lists
|
||
1280×480, which is 1920×720 at 240 dpi in dp; upstream reported a 1778×720 window); FIRMWARE.md
|
||
lists all three until a photo of that line settles it.
|
||
- 2026-10-18 — EVProfile's licence checked: MIT, forked from SliDeeN/MG4Control and last merged
|
||
from it on 2026-08-02, inside MG4Control's MIT period (MIT up to 2.6.5 of 2026-08-05, GPL v3
|
||
from 2.6.6 of 2026-09-02). Recorded in `LICENSE.md`. History scanned for secrets before the
|
||
repository went public: none.
|
||
- 2026-10-18 — Licence: Leon Kernan's `ABRP_Uploader` has been MIT since 2026-07-29 (its commit
|
||
`75d8f2bc`), so the "unresolved" notes are gone. `LICENSE` names every copyright holder in the
|
||
fork chain; `LICENSE.md` explains that `EVHardware/` is PolyForm Noncommercial, which makes the
|
||
built APK non-commercial. README, AGENTS.md and CONTRIBUTING.md updated.
|
||
- 2026-10-18 — App 2.16.1: the Overview reads the car every 0.5 s (was 2 s), on a background
|
||
thread; the consumption and charging trackers stay on the 2-second tick (they integrate over
|
||
time, and the charging tracker writes its session to storage on every sample). Note for
|
||
emulator tests: uiautomator can no longer dump the screen while the Overview is open, since
|
||
the UI is never idle.
|
||
- 2026-10-18 — Grafana's read-only InfluxDB token without a manual step: an init container in
|
||
the Grafana pod creates it with the admin token at every start, deletes the previous one and
|
||
leaves only the read-only token in a memory volume; the datasource reads it with
|
||
`$__file{/run/influx/token}`. InfluxDB ignores chosen token values (tested), so it cannot come
|
||
from the Secret. The `grafana-influx` Secret is gone. Tested in podman: InfluxDB 2.7.12, the
|
||
script as the Grafana user (twice: rotates, no pile-up), Grafana 13.2.3 datasource healthy.
|
||
- 2026-10-18 — App 2.16.0, server 2.6.0: over-the-air updates from this repository. `publish-car`
|
||
creates the pre-release `car-<version>` on the private forge with the APK and its `.sha256`;
|
||
the server relays releases and attachments to the car (`/v1/app/…`, read with a read-only
|
||
`FORGE_TOKEN`); the app checks them through its server with its own token, allows exactly the
|
||
server's host, downloads itself (no DownloadManager, so the token stays in the app), verifies
|
||
SHA-256 and package, and tries a PackageInstaller session before falling back to Downloads.
|
||
`OTA.md` rewritten for this flow. Not yet tried on the car.
|
||
- 2026-10-18 — App 2.15.0: unsent live samples survive a restart. The queue is saved to
|
||
`files/telemetry_queue.json` (AtomicFile) at most every 5 minutes and only after a change, and
|
||
at once when the service stops; it is restored, in order and within its 2000-sample cap, when
|
||
the service starts. A killed process loses at most 5 minutes. Tested on the emulator across a
|
||
reboot (6 queued samples back).
|
||
- 2026-10-18 — App 2.14.4: every UI text is in `strings.xml` (Charging, Consumption and Settings
|
||
labels, toasts, the trip-reset dialog, charging and Wi-Fi states, the daily summary, the "—"
|
||
placeholder); lint reports no hard-coded text. Upload-log entries stay in code as log text.
|
||
- 2026-10-18 — Translated the rest: `mise.toml`, the comments in `app/build.gradle`, `OTA.md`'s
|
||
message table (now the updater's current English messages), the Turkish messages quoted in
|
||
`CHANGELOG.md` and `OTA.md` (glossed), two test messages, the ignition-state comment in
|
||
`EVHardware.kt`.
|
||
- 2026-10-18 — About dialog: link and QR code point to https://git.stinnesbeck.com/nils/mg4
|
||
(were Fatih's GitHub). Status section brought up to date.
|
||
- 2026-10-18 — App 2.14.2: "Range, 5-min average", like the usage and power cards (was "Range at 5-min average").
|
||
- 2026-10-18 — App 2.14.1: Overview values and their notes right-aligned, with tabular
|
||
(equal-width) digits, so 9 → 10 → 100 no longer shifts the text; labels stay left.
|
||
- 2026-10-18 — App 2.14.0: accent colour selectable after the MG4 paints (Settings → Display):
|
||
Dynamic Red (default), Volcano Orange, Holborn Blue, Racing Green, Camden Grey, Monument Silver,
|
||
Arctic White, Black Pearl; white, silver and black become light greys and a pearl blue-grey,
|
||
since they cannot be an accent on black. The accent palette is theme attributes
|
||
(`attrs_accent.xml`, overlays in `accents.xml`); a change recreates the activity on the same
|
||
page. Android 9 crashes on gradients with theme colours, so the battery card is layered.
|
||
Logs tab: "Look for the car colour" lists system properties that mention colour, paint or car
|
||
model, to find out whether the paint colour can be read (nothing on the emulator).
|
||
- 2026-10-18 — App 2.13.0: tyre pressures in bar by default, kPa or psi selectable (Settings →
|
||
Display; display only, the car and the server keep kPa). "Power, 5-min average" card under
|
||
Power: time-weighted mean battery power, standing included.
|
||
- 2026-10-18 — App 2.12.1: no scroll bars (the 2-second refresh kept them from ever fading);
|
||
"Energy since start" tile (net kWh of the trip, recuperation counted back) left of "Usage since
|
||
start".
|
||
- 2026-10-18 — App 2.12.0, server 2.5.0: settings entered on a phone. "Enter on phone" shows a QR
|
||
code; the phone form on the server (approval password) takes the ABRP credentials, charging
|
||
price and usable capacity, and the car collects them once (RFC 8628-style, like pairing; values
|
||
in server memory only, at most 10 minutes). The config file also takes `charging_price` and
|
||
`usable_kwh`. The QR dialog is shared with pairing (`PhoneQrDialog`). Tested end to end with
|
||
curl against the server image and InfluxDB; the app side on the emulator only up to the dialog.
|
||
- 2026-10-18 — App 2.11.1, server 2.4.0: ABRP forwarding removed from the server. The car and the
|
||
server use the same mobile connection, so a server fallback for ABRP bought nothing. The car no
|
||
longer sends `abrpSentUtc`; the server ignores unknown fields, so older apps keep working.
|
||
- 2026-10-18 — App 2.11.0, server 2.3.0:
|
||
- Live data goes to ABRP straight from the car when ABRP credentials are set in the app
|
||
(Live data tab or config file: `abrp_api_key`, `abrp_token`), so ABRP keeps getting it
|
||
while the server is down. The car tells the server the newest sample ABRP took
|
||
(`abrpSentUtc`); the server forwards only newer ones. Without credentials in the app the
|
||
server forwards as before. This deliberately puts ABRP credentials in the car again
|
||
(encrypted, never logged).
|
||
- Settings regrouped: Live data (on/off, ABRP, frequency), Server, Settings (full screen, voice
|
||
key, usable capacity and SoH from Consumption, charging price from Charging, Wi-Fi, which
|
||
lost its own tab). Charging and Live data have their own icons.
|
||
- 2026-10-18 — App 2.10.2: the range shows "∞" instead of "> 999 km" while recuperating.
|
||
- 2026-10-18 — App 2.10.1: net regeneration (recuperating downhill) shows as negative usage, and
|
||
the range as "> 999 km" with "while recuperating", instead of "needs a few minutes of driving".
|
||
- 2026-10-18 — App 2.10.0: "Range since start" replaces the range at current power. It uses
|
||
the consumption since the car was switched on, including standing with the climate on, and a
|
||
new trip starts after 10 minutes without samples or when charging. Usage (kWh/100 km) for both
|
||
estimates moved into cards of their own under the range tiles, which fit on the full-screen
|
||
1920x720 overview without scrolling.
|
||
- 2026-10-18 — Code review fixes. App 2.9.0, server 2.2.0, Grafana token:
|
||
- The server drops a bad value (or a sample without a plausible time) instead of rejecting
|
||
the batch, and answers 200 with what it dropped; the app sends only values within the
|
||
server's ranges (speed as a magnitude) and no samples before its clock is set. Before, one
|
||
reversing sample cost the whole queued batch.
|
||
- Pairing: the token is handed out and accepted only after the device is saved; wrong-password
|
||
lockouts and open-request limits are per client address; a paired token only serves its
|
||
own installation id (403 otherwise).
|
||
- ABRP forwarding skips samples stamped ahead of the server's clock, which used to block it.
|
||
- App: settings written in plaintext during a keystore failure move back into encrypted
|
||
storage; the installation id is derived from ANDROID_ID, so a reinstall keeps its history;
|
||
the imported config file is deleted; GPS stops when live telemetry is off and pauses while
|
||
parked and idle; range estimates use the monotonic clock; one HTTP helper (`ServerHttp`)
|
||
for all server calls; the last Turkish strings translated.
|
||
- Grafana reads InfluxDB with its own read-only token (`grafana-influx` Secret, created by
|
||
hand: `k8s/README.md`).
|
||
- 2026-10-18 — README rewritten for Nils' MG4. `OTA.md`, `mise.toml` and
|
||
`docs/MG4_PLATFORM_INSTALL.md` use the new package ID and `NilsMG4-*.apk` names; `OTA.md` notes
|
||
that updates are switched off. `CHANGELOG.md` keeps the old ID as upstream history.
|
||
- 2026-10-18 — App 2.8.2: full screen uses the whole display. The MG4 hid its bars but left a black
|
||
band where they were: LAYOUT_STABLE kept reporting the bars' insets and the fitsSystemWindows root
|
||
padded for them. Both are off in full screen now.
|
||
- 2026-10-17 — App 2.8.1: three-part version names (`2.8.1-unstable`, `NilsMG4-unstable-2.8.1.apk`).
|
||
Upstream's fourth part was its CI build number; the version code keeps the x100000 factor so it
|
||
never falls below codes already installed.
|
||
- 2026-10-17 — App 2.8.0: immersive full screen (switch in the Upload tab, default on). Hides the
|
||
system bars on the emulator; whether SAIC's own bars on the MG4 can be hidden is only known once
|
||
tried on the car.
|
||
- 2026-10-17 — App 2.7.1: the voice key works. The car's key report, read off the on-screen log:
|
||
`android.intent.extra.hardkey.keycode` (Integer), `.down` (Boolean, a report per press and per
|
||
release), `.longpress` (Boolean). 2.7.0 looked only for short names and never found the code.
|
||
Floating button removed (it did not show over the MG4 launcher).
|
||
- 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`.
|