Skip to content

Wrap BLE logs with the phone's acquisition time and log VCSEC status - #148

Merged
dzid26 merged 3 commits into
mainfrom
work/ble-log-envelope
Oct 8, 2026
Merged

dzid26 merged 3 commits into
mainfrom
work/ble-log-envelope

Conversation

@dzid26

@dzid26 dzid26 commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Wraps every logged BLE reply in one envelope, BleRecord, and logs VCSEC status through it. This is the status-log half of #85, split out to land first. #149 (DriveState) is stacked on it.

  • Envelope (core/src/main/proto-teslable/ble_record.proto). Fields follow a reading's order: acquired_at (1, the phone's clock at arrival), then a payload oneof with the car's raw reply, verbatim: vehicle_status (2) or charge_state (3). DriveState is field 4 in Log DriveState in the BLE envelope #149. Car timestamps are never touched. The proto header and ADR-0008 carry the evolution rules (additive only), and a test pins the tags.
  • Charge log (<vehicleId>.charge.pblog). Every reply is logged raw. One without the car's timestamp or a level stays in the log but off the chart (owner: keep all raw).
  • Status log (<vehicleId>.vcsec.pblog). It is timed by acquired_at, since VCSEC has no clock. A reading is logged when it is the first, the first after a (re)connect, or a change; otherwise every 15 min. When the link drops, the newest skipped reading is logged too (StatusLogGate), so each observed stretch ends at the last reading before the phone lost the car.
  • ProtoLog now skips a complete record that fails to decode, instead of stopping, so records from incompatible test builds can't hide later ones. A truncated tail still ends the read.
  • Controller. One clock read per reply feeds acquiredAtMillis, and the disconnect path calls historyStore.onLinkLost.
  • Docs: ADR-0008 (BLE log envelope), the ADR-0006 pointer, PRIVACY.md, the master plan, and two docs that named the old charge file.

Decisions

  • Old charge logs reset. Raw <vehicleId>.pblog files are no longer read, and not deleted, so the chart starts empty after the update. No migration code.
  • Test-build files of this PR (other field numbers, or .status.pblog) are skipped by the readers, so nothing needs deleting.

Type

  • Bug fix
  • Feature
  • Protocol / crypto change
  • Docs / CI / tooling
  • Refactor (no behavior change)

Testing

  • Full build at a5234e6: :core:test (169), :app:testDebugUnitTest (21), :app:assembleDebug, :app:lintDebug, ktlint and detekt. New tests cover the envelope mapping, the wire tags, StatusLogGate (hold back, link-lost hand-over, reconnect, seeding), and ProtoLog skipping undecodable and empty frames.
  • Real car: not tested. Check that the status file grows on changes and every 15 min, and gets a last record when you walk out of range.

Checklist

  • Protocol changes ship test vectors; expected.txt regenerated, not hand-edited (n/a: storage format only)
  • No VINs, keys, or decrypted payloads in logs
  • Docs/master-plan updated where relevant
  • New source files carry SPDX-License-Identifier: AGPL-3.0-only

🤖 Generated with Claude Code

https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX

claude added 2 commits October 8, 2026 07:53
Every logged BLE reply is now wrapped in one envelope, BleRecord: the
phone's clock when the reply arrived (acquired_at) next to the car's raw
message, verbatim, as a oneof payload (charge_state, vehicle_status;
DriveState joins as field 4 later). Car timestamps are never touched.
Charge samples stay on ChargeState.timestamp (ADR-0006: never filled in;
a record without it or without a level is still not logged), and VCSEC
status, which has no clock, is timed by acquired_at. The proto lives in a
second Wire source root, core/src/main/proto-teslable, so a re-vendor of
Tesla's protos never touches it, and its header carries the evolution
rules that keep old records readable.

The controller reads the phone's clock once per incoming reply and hands
it to HistoryStore.record (charge) and recordStatus. A status reading is
logged when it is the first for the vehicle, the first after a
(re)connect, or changed; otherwise every 15 minutes, so a sleeping car
with a phone connected is distinguishable from a phone out of range
(shouldLogStatus, a pure core function). TeslaVcsec.Status now carries
the raw VehicleStatus, like TeslaCommands.Charge.raw.

HistoryStore keeps one file per vehicle per kind through one VehicleLogs
helper with the same 20k cap and trim: <vehicleId>.charge.pblog and
<vehicleId>.status.pblog. BleRecord.toBatterySample and toStatusSample
derive the read models; StatusSample is unchanged. The debug demo seeds
the new charge file.

Pre-1.0 reset: the old raw <vehicleId>.pblog charge files are no longer
read or written, and are not deleted, so the history chart starts empty
after the update. No migration code runs.

Docs: ADR-0008 (BLE log envelope), a pointer in ADR-0006, PRIVACY.md, the
master plan, and the two docs that named the old charge file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
A build of the original #85 branch wrote an earlier record format to
<vehicleId>.status.pblog. The envelope reader stops at the first such
record, so that file would hide every later status reading. A new name
leaves the old file ignored, like the old raw charge logs, with no
manual delete before testing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
dzid26 pushed a commit that referenced this pull request Oct 8, 2026
#148 replaces this branch's StatusObservation status log with the
BleRecord envelope (acquired_at, one file per vehicle per kind, status in
<vehicleId>.vcsec.pblog). Every status-log file takes #148's version, and
this branch's superseded status_observation.proto, StatusObservationMapping
and its test, and the first ADR-0008 draft are removed. What remains on top
of #148 is the parked-drain projection: statusHistory on the controller,
HistorySamples, the projection and its tests, the car screen wiring, README
and the master-plan projection line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Screenshots

App screenshots: light and dark

Captured from the demo build at 3f46af5.

Debug APK: zip from Android CI (GitHub sign-in needed)

@dzid26 dzid26 mentioned this pull request Oct 8, 2026
7 of 12 tasks
Review on #148:

- BleRecord fields now follow a reading's order: acquired_at (1),
  vehicle_status (2), charge_state (3), with DriveState as 4 next. A test
  pins the tags. Records from pre-merge test builds, numbered the other way,
  decode as the wrong kind or not at all: the read models skip records
  without their payload, and ProtoLog now skips a complete record that
  fails to decode (and empty frames) instead of stopping, so the records
  after it still read. A truncated tail still ends the read.
- A status reading the policy skipped is held back, and logged when the
  link to the car drops (StatusLogGate in core, called from the
  controller's disconnect path). Each observed stretch then ends at the last
  reading before the phone lost the car instead of up to 15 minutes earlier.
- Every charge reply is logged raw, including one without the car's
  timestamp or a level; those stay off the chart (owner: keep all raw).

ADR-0008 and the ADR-0006 pointer record all three.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
dzid26 pushed a commit that referenced this pull request Oct 8, 2026
#148 now orders the envelope as time, status, charge (fields 1-3), so
drive_state stays field 4 after charge_state, and the field-order test
covers all four in place of the old numbering test. #148's link-lost status
flush and raw charge logging merge in unchanged.

Drive replies follow the same owner rule as charge (keep all raw): every
reply is logged, including one without the car's own timestamp, which stays
out of driveSamples. ADR-0008 records both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Screenshots

App screenshots: light and dark

Captured from the demo build at a5234e6.

Debug APK: zip from Android CI (GitHub sign-in needed)

@dzid26
dzid26 merged commit 918487f into main Oct 8, 2026
5 checks passed
@dzid26
dzid26 deleted the work/ble-log-envelope branch October 8, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants