Commit Graph
54 Commits
Author SHA1 Message Date
nilsandClaude Opus 5.5 8a5813fa16 App 2.4.2: pairing dialog shows errors instead of closing
- On a failure the pairing dialog stays open and shows the reason in
  red (Cancel becomes Close); before, it closed at once and the reason
  was only in the Server tab's status line.
- "Pairing is not enabled" only when the server says so. Any other 404
  (a server older than 2.1.0, or a proxy's page) is reported as a
  server without pairing.
- Pairing and Test connection name the problem: a server name that
  does not resolve (Test connection used to say "No internet
  connection"), a server that does not answer, or the HTTP status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
2026-10-17 20:56:02 +02:00
nilsandClaude Opus 5.5 315586f94c Move the server to car.k8s.stinnesbeck.com
The server's host is car.k8s.stinnesbeck.com, not car.stinnesbeck.com:
IngressRoute, PUBLIC_URL (so the pairing QR code points there), the
app's pre-filled server URL and hint, and the docs. Grafana's
placeholder host follows to car-grafana.k8s.stinnesbeck.com, so one
*.k8s.stinnesbeck.com default certificate covers both. App 2.4.1 for the
new default; the server image is unchanged (its URL comes from the
manifest).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
2026-10-17 18:08:35 +02:00
nilsandClaude Opus 5.5 c578a882bd App 2.4.0: pair with the server by QR code
The Server tab gets "Pair with phone" right under the server URL, which
is now pre-filled with https://car.stinnesbeck.com. It opens a dialog
with a QR code and the short code, polls the server, and on approval
saves this car's own token like Save would — nothing to type in the
car. Denied, expired, and pairing switched off on the server are each
reported. PairingClient is tested against a stand-in for the server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
2026-10-17 16:16:52 +02:00
nilsandClaude Opus 5.5 e874578e54 Replace the turquoise with MG red tones
ev_cyan becomes a light red (#FF9A9E, for values and secondary
highlights, readable on black) and ev_cyan_deep a deep red (#3A1215,
version badge). Turquoise hardcoded in the battery card, switch track,
input panel, car outline, background glow and charging-graph grid is
replaced with matching red tones, as are two gold leftovers (tab
outline, second background glow). The mint "OK" status colour and the
amber charging curve are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
2026-10-17 15:45:18 +02:00
nilsandClaude Opus 5.5 538d03361d Translate the dashboard to English; euro instead of lira
Dashboard: every label, empty state, chart legend and the login page
are English; numbers and dates use en-GB (24-hour clock, day-month
dates), money is EUR, percentages read 80% instead of %80, durations
"2 h 05 min". html lang is en. Server version 1.1.2; manifests pin it.

Car app: the charging tab's price suffix, validation message and total
cost use € instead of ₺.

Checked by rendering all three pages from the 1.1.2 image against
InfluxDB 2.7.12 in podman.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
2026-10-17 14:39:59 +02:00
nilsandClaude Opus 5.5 e39d9cdcf9 Replace the muted gold accent with MG red
ev_accent is now #E0424C, the red of the launcher icon (about 4.6:1 on
the black background, enough for accent-coloured text; the darker MG
brand red is about 3:1). ev_on_accent and ev_gold_surface follow with a
dark red tint; the gold hardcoded in two drawables and the consumption
period buttons is replaced too. AGENTS.md's visual direction updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
2026-10-17 14:29:01 +02:00
nilsandClaude Opus 5.5 6145736c97 App 2.3.0: Server tab, live telemetry to the car server
- New Server tab: server URL, ingest token and installation id, with
  Save, Test connection (health, then a read-only token check) and the
  config-file import that upstream removed (app folder / USB stick first,
  document picker as fallback). Settings are kept encrypted and read on
  every use, so the service picks up a change without a restart. Each
  installation generates its own id; the inherited upstream UUID and
  the baked-in CONSUMPTION_API_* build fields are gone.
- Live telemetry restored from before ea8032a (TelemetryPayload,
  UploadCadence, UploadSettings, GPS) and sent in batches to
  POST /v1/telemetry instead of ABRP; the server forwards to ABRP. Adds
  ext_temp, voltage and current. Unsent samples queue in memory (about
  8 h) while offline.
- Upload tab: the live-telemetry switch and cadence controls work again;
  the dead service and autostart switches are removed.
- Manifest: location permissions and foreground service types.
- Unit tests compile again: 114 (stable) / 136 (unstable) pass. Tests
  for the deleted AbrpApi and HeadUnitWeather removed; new tests for
  ServerConfig and TelemetryQueue. Lint passes.

Not yet tried on the car.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 14:23:04 +02:00
nilsandClaude Opus 5.5 10029623e3 Remove on-screen diagnostics from the Logs tab
They served their purpose: vehicle values show on the car. This reverts
commit b2a5b32.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:38:36 +02:00
nilsandClaude Opus 5.5 b2a5b32b08 Add on-screen diagnostics to the Logs tab
Without adb there was no way to see why vehicle values stay blank:
refreshVehicle() swallowed every exception. The Logs tab now shows the
package, version, UID (and whether it is the system UID), signer
SHA-256, firmware, car permission grants, the last snapshot and read
error, and the newest 200 entries of the vehicle library's AppLogger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:26:39 +02:00
nilsandClaude Opus 5.5 a707b590f7 Rename package and APKs to NilsMG4, credit Fatih, add SPECS.md
- applicationId com.evsuite.abrp -> com.stinnesbeck.nilsmg4 (the car
  build is .unstable); the Java namespace is unchanged. Installs next to
  upstream's app rather than over it.
- APK names NilsMG4-*.apk, debug builds included; build-car follows.
- OTA install-result action, cache/download names, User-Agent and log
  tags follow the new name.
- About credits fatihdonmezdev / FatihsMG4.
- SPECS.md journals what changed and what is still to do, starting
  with pointing the About link and QR code at git.stinnesbeck.com/nils/mg4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:08:09 +02:00
nilsandClaude Opus 5.5 3da3fa9f49 Rename app to "Nils' MG4"
Changes the launcher label, header brand caption, About title, logo
description and OTA download labels. Package ID, APK file names, log
tags and the repository link are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:02:18 +02:00
nilsandClaude Opus 5.5 3af337ca89 Disable OTA update checks by pointing at an unresolvable host
The updater stays in place, but its releases URL now uses the .invalid
TLD (RFC 2606), so checks fail instead of offering upstream's Turkish
builds over this one. Swap in a fork's releases API to re-enable it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 12:59:52 +02:00
nilsandClaude Opus 5.5 864ac612da Translate app UI from Turkish to English
Translates string resources, hardcoded layout text and user-facing
strings in Java sources (toasts, dialogs, status labels, upload log).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 11:38:00 +02:00
TCFDONMEZandClaude ed5e38bdb8 fix: lifetime optional in validation + type — app no longer sends it
validateConsumption required lifetime, but the app stopped sending it
(backend now derives it). asObject(undefined) threw 'body is required',
rejecting every chunk. Now lifetime is optional in both the validator
and the DailyConsumption type.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-09 23:49:57 +03:00
TCFDONMEZandClaude 06265bacc9 simplify: backend derives lifetime from day totals, app stops sending it
- backend daily PUT: no longer $set lifetime (app's local counter is unreliable after APK wipe)
- backend history GET: derives lifetime as sum of all days' totals, returns it at response level
- app: stops sending lifetime in chunks; reads it from history response
- app: refresh button on consumption pane + 'Şimdi yükle' on log pane (manual cache flush)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-09 21:41:15 +03:00
TCFDONMEZ ea8032a813 chore 2026-10-09 17:31:08 +03:00
TCFDONMEZ 056fc2a5bd Sync today's driving totals every 30 minutes 2026-10-08 16:46:47 +03:00
TCFDONMEZ ae036b9d65 Count driving only and include charging losses 2026-10-08 15:51:38 +03:00
TCFDONMEZandClaude Opus 5 050fe84d6a Release 2.2.37 with cloud sync configured
The published APK carries the Vercel origin and the write-only ingest token.
Verified against the signed artifact: the origin and token are present, and no
MongoDB URI, host or password appears anywhere in the dex.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-08 14:43:49 +03:00
TCFDONMEZandClaude Opus 5 e9bfa1217b Meter DC charging only, on a monotonic clock
The charging session meter used the head unit's wall clock, which jumps when it
picks up GPS or NTP time. One forward jump mid-charge invented both kWh and
hours. The consumption counters already integrate on elapsedRealtime for exactly
this reason; the session meter now does too, and keeps wall-clock values only as
the start and end stamps a reader needs to place a session on a timeline.

Intervals longer than five minutes are no longer bridged. A head unit that slept
mid-charge came back and billed the whole gap at the last power reading, and a
session interrupted overnight reported the night as charging time. Reported
duration is metered charging time now, not the span from start to end.

Sessions are metered on DC alone, at or above 10 kW. AC charging on this car is
an overnight affair the head unit sleeps through, so its curve, duration and
energy were guesses either way. The charging screen says so rather than letting
the previous DC session's figures be read as the current AC charge.

Also:

- A daily consumption record uploaded after an offline spell carries the
  lifetime total as it stood at the close of that day. Every backlogged day used
  to be stamped with the current lifetime, which plots as a flat line and one
  cliff.
- The offline session queue is capped at 50. It is a single SharedPreferences
  string read into memory in full, and nothing drained it while the cloud
  endpoint was unconfigured.
- A long charge keeps the start of its curve: at 720 points the sample interval
  doubles and the curve is thinned, rather than dropping the oldest point.
- The cloud sync throttle runs on the monotonic clock, and catch-up sends up to
  10 days and 10 sessions per cycle rather than one and three.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-08 14:20:29 +03:00
TCFDONMEZ 1a37e52eda Track charging sessions and daily consumption history 2026-10-08 13:39:40 +03:00
TCFDONMEZ 9a6c121d09 Rename app and persist all-time consumption to MongoDB 2026-10-08 13:23:00 +03:00
TCFDONMEZ 90982580af Allow disabling ABRP uploads independently 2026-10-05 00:28:28 +03:00
TCFDONMEZ dbbfb9f803 Fix consumption tracking while charging 2026-10-02 11:15:22 +03:00
TCFDONMEZ ecbee4980c Match DriveHub lifetime consumption calculation 2026-09-30 23:24:15 +03:00
TCFDONMEZ 74723335e1 Remove automatic consumption reset migration 2026-09-30 23:10:03 +03:00
TCFDONMEZ eb40b3d31d Use vehicle consumption counter for energy 2026-09-30 23:06:27 +03:00
TCFDONMEZ d25c09ec8f Correct SWI69 distance scale 2026-09-30 13:46:51 +03:00
TCFDONMEZ c44722bf12 Retain four months of selectable daily consumption 2026-09-30 12:12:25 +03:00
TCFDONMEZ 1709fdf1ac Track rolling 7 and 30 day consumption 2026-09-30 12:08:54 +03:00
TCFDONMEZ 81483e7f48 Add weekly and monthly consumption periods 2026-09-30 12:04:40 +03:00
TCFDONMEZ 1749815599 Remove unavailable remaining energy card 2026-09-30 11:34:09 +03:00
TCFDONMEZ 48a210db9b Add readable charging curve and SOC power table 2026-09-30 11:30:13 +03:00
TCFDONMEZ 76d1d680d6 Use consumption period buttons 2026-09-30 11:28:24 +03:00
TCFDONMEZ 634a374ea7 Download one OTA APK and open Downloads 2026-09-30 11:27:14 +03:00
TCFDONMEZ 25abf0da5b Add persistent consumption and charging dashboards 2026-09-30 11:17:54 +03:00
TCFDONMEZ 69ef0eeef6 ui: hide unsupported vehicle telemetry 2026-09-30 11:07:29 +03:00
TCFDONMEZ e8c9122fdf fix: use DownloadManager for OTA packages 2026-09-30 11:02:08 +03:00
TCFDONMEZ e091704990 ui: show installed version beside OTA button 2026-09-30 10:55:21 +03:00
TCFDONMEZ dde5842c62 chore: publish public installer OTA test 2026-09-30 10:49:55 +03:00
TCFDONMEZ 1bce1d1ae1 fix: stage OTA fallback in public downloads 2026-09-30 10:48:25 +03:00
TCFDONMEZ 08e894b6f2 chore: publish OTA fallback test build 2026-09-30 10:43:27 +03:00
TCFDONMEZ f2e201d293 fix: open system installer when OTA session fails 2026-09-30 10:42:12 +03:00
TCFDONMEZ cca911c4e4 ux: disable page transition animation 2026-09-30 10:36:09 +03:00
TCFDONMEZ bad747a230 fix: align OTA install with DriveHub Dort 2026-09-30 10:29:57 +03:00
TCFDONMEZ 1330c0b723 chore: release 2.2.9 OTA test 2026-09-30 10:24:41 +03:00
TCFDONMEZ 48a7a0dd72 feat: add OTA 2026-09-29 17:04:59 +03:00
TCFDONMEZ 511c166436 feat: add ota and ui changes 2026-09-29 14:32:25 +03:00
TCFDONMEZandClaude Opus 5 c102625306 chore: release 2.2.4
2.2.3 was published with the broken installer, so it is recorded as released
rather than rewritten, and 2.2.4 is the build that can actually install itself.

2.2.4 must be sideloaded once — the fix cannot deliver itself over the path it
fixes. OTA should carry 2.2.5 onward.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-25 14:03:18 +03:00
TCFDONMEZandClaude Opus 5 091d26d6d6 fix: install OTA updates through PackageInstaller
The refresh button found 2.2.3, downloaded it, and reported
"Kurulum reddedildi (imza uyuşmuyor?)" for a build signed with exactly the same
platform certificate as the one already running. The signature was never the
problem.

install() shelled out to `pm install -r` with a path inside the app's private
cache. pm does not read the file — it hands the path to the package manager
service, which opens it from its own process and SELinux context, where that
directory is not readable. The install failed for a reason nobody could see:
install() returned a bare boolean, and UpdateHook mapped every false to the one
message it had. The pm output that would have named the real error was captured
into a StringBuilder and discarded.

A PackageInstaller session takes no path. We open the archive ourselves, as the
app, and stream it into the session, so no second process ever has to reach our
cache. Approach taken from merthankaraman/DriveHub_Dort, which hit the same wall
on the same head unit.

Consequences worth knowing when reading a future failure:

- Committing a session is asynchronous, so the refresh button can only say an
  install started. OtaInstallResultReceiver has the verdict, and surfaces the
  package manager's own INSTALL_FAILED_* text rather than guessing.
- STATUS_PENDING_USER_ACTION is not an error. The receiver launches the
  confirmation activity the platform supplies.
- The PendingIntent must be MUTABLE or it arrives with no status extras.

install() now returns an enum, so a certificate mismatch, an unparseable or
wrong-package archive, and a failed session each say what they are.

Also checks the archive's package name against ours, which was a genuine gap:
the platform key signs every app on this unit, so a certificate match alone
never meant the APK was this app.

The `pm install -r` exit-code test goes with the shell-out it covered. Nothing
in the session path is exercisable off-device.

REQUEST_INSTALL_PACKAGES and the receiver are declared in the unstable manifest
only — the stable channel has no updater at all, and its merged manifest was
checked to confirm it gains neither.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-25 14:02:55 +03:00