Skip to content

Commit fd449dd

Browse files
committed
feat(telemetry): add Fleet fields 260-269
Vendor the latest Fleet Telemetry fields, generate metadata and routing, normalize fixed kWh and mph values to SI, and surface new vehicle, security, navigation, and software-update signals in the live UI.
1 parent c7b600d commit fd449dd

19 files changed

Lines changed: 320 additions & 25 deletions

File tree

api/proto/tesla/CHECKSUM

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4596EBA1D26B72EB69BBF747831BF0EB2EEC106A54048D9BC7D6DE765446697C
1+
E8BE013478FC24DADE3B40DF10A269E6477D27FABB95C641A575A5214A101618

api/proto/tesla/SOURCE.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ rationale.
1717
|---|---|
1818
| Upstream repository | `github.com/teslamotors/fleet-telemetry` |
1919
| Upstream path | `protos/vehicle_data.proto` |
20-
| Upstream tag | **v0.8.0** |
21-
| Upstream commit | `031553a7d3d6952c1552ed13dc71aaf4fd4a882b` |
22-
| Upstream commit date | 2025-12-12 |
23-
| Upstream raw URL | <https://raw.githubusercontent.com/teslamotors/fleet-telemetry/031553a7d3d6952c1552ed13dc71aaf4fd4a882b/protos/vehicle_data.proto> |
24-
| Upstream tree URL | <https://github.com/teslamotors/fleet-telemetry/blob/v0.8.0/protos/vehicle_data.proto> |
25-
| File size | 21,291 bytes |
26-
| SHA256 | `4596EBA1D26B72EB69BBF747831BF0EB2EEC106A54048D9BC7D6DE765446697C` |
27-
| Fetched on | 2026-05-01 |
20+
| Upstream tag | untagged (`Add new streamable fields 260-269`, #546) |
21+
| Upstream commit | `8fbaa100bd365936dab6ecbf0e2d7070c4d765cb` |
22+
| Upstream commit date | 2026-09-14 |
23+
| Upstream raw URL | <https://raw.githubusercontent.com/teslamotors/fleet-telemetry/8fbaa100bd365936dab6ecbf0e2d7070c4d765cb/protos/vehicle_data.proto> |
24+
| Upstream tree URL | <https://github.com/teslamotors/fleet-telemetry/blob/8fbaa100bd365936dab6ecbf0e2d7070c4d765cb/protos/vehicle_data.proto> |
25+
| File size | 21,729 bytes |
26+
| SHA256 | `E8BE013478FC24DADE3B40DF10A269E6477D27FABB95C641A575A5214A101618` |
27+
| Fetched on | 2026-09-14 |
2828
| Vendored by | phase-42 prompt 0010 |
2929
| Reviewed by | Staff Engineer + Principal Engineer + Principal Architect (per ADR-004) |
3030

api/proto/tesla/VERSION

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
teslamotors/fleet-telemetry@v0.8.0
2-
commit=031553a7d3d6952c1552ed13dc71aaf4fd4a882b
3-
fetched=2026-05-01
1+
teslamotors/fleet-telemetry@8fbaa100bd365936dab6ecbf0e2d7070c4d765cb
2+
commit=8fbaa100bd365936dab6ecbf0e2d7070c4d765cb
3+
fetched=2026-09-14

api/proto/tesla/vehicle_data.proto

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,19 @@ enum Field {
284284

285285
MilesSinceReset = 258;
286286
SelfDrivingMilesSinceReset = 259;
287+
288+
// fields 260-269 are first available in firmware version 2026.32 (device client version 1.3.0)
289+
290+
GpsAccuracyMeters = 260;
291+
LifetimeEnergyChargedKwh = 261;
292+
BrickSocMinPercent = 262;
293+
NominalFullPackEnergyKwh = 263;
294+
GradeEstimatePercent = 264;
295+
MaxSpeedToReachDestinationMph = 265;
296+
SoftwareUpdateAvailable = 266;
297+
SoftwareUpdateInProgress = 267;
298+
RemoteStartActive = 268;
299+
SemiCruiseSpeedLimitMph = 269; // Semi-truck only
287300
}
288301

289302
// ChargingState is deprecated and not used

cmd/protogen-tesla/emit.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,29 @@ func classifyExplicit(name string) fieldClass {
569569
return fieldClass{cat: "safety_security", kind: "float", unit: "distance", isSettingUnit: false}
570570
case "SelfDrivingMilesSinceReset":
571571
return fieldClass{cat: "safety_security", kind: "float", unit: "distance", isSettingUnit: false}
572+
case "GpsAccuracyMeters":
573+
// Wire unit is already metres (name-encoded). Do not treat as
574+
// SettingDistanceUnit-following distance — ToSI would re-scale.
575+
return fieldClass{cat: "location", kind: "float", unit: "none", isSettingUnit: false}
576+
case "LifetimeEnergyChargedKwh":
577+
return fieldClass{cat: "charging", kind: "float", unit: "none", isSettingUnit: false}
578+
case "BrickSocMinPercent":
579+
return fieldClass{cat: "charging", kind: "float", unit: "charge", isSettingUnit: false}
580+
case "NominalFullPackEnergyKwh":
581+
return fieldClass{cat: "charging", kind: "float", unit: "none", isSettingUnit: false}
582+
case "GradeEstimatePercent":
583+
return fieldClass{cat: "driving", kind: "float", unit: "none", isSettingUnit: false}
584+
case "MaxSpeedToReachDestinationMph":
585+
// Name-encoded mph; converted to m/s via fixedMphSpeedFields.
586+
return fieldClass{cat: "driving", kind: "float", unit: "none", isSettingUnit: false}
587+
case "SoftwareUpdateAvailable":
588+
return fieldClass{cat: "vehicle_state", kind: "bool", unit: "none", isSettingUnit: false}
589+
case "SoftwareUpdateInProgress":
590+
return fieldClass{cat: "vehicle_state", kind: "bool", unit: "none", isSettingUnit: false}
591+
case "RemoteStartActive":
592+
return fieldClass{cat: "vehicle_state", kind: "bool", unit: "none", isSettingUnit: false}
593+
case "SemiCruiseSpeedLimitMph":
594+
return fieldClass{cat: "driving", kind: "float", unit: "none", isSettingUnit: false}
572595
}
573596
return fieldClass{}
574597
}

internal/tesla/normalize/normalize.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ func (p *Pipeline) toSI(ctx context.Context, atomic codec.Atomic, vehicleIntID i
149149
// kWh/kW. Neither family may be dropped because a vehicle has no
150150
// unit_history row.
151151
if units.IsFixedMileDistanceField(atomic.Field) ||
152-
units.IsFixedKiloToBaseField(atomic.Field) {
152+
units.IsFixedKiloToBaseField(atomic.Field) ||
153+
units.IsFixedMphSpeedField(atomic.Field) {
153154
raw, ok := coerceFloat(atomic.Value)
154155
if !ok {
155156
return codec.Atomic{}, fmt.Errorf("%w: %s value of type %T not coercible to float64", units.ErrUnsupportedField, atomic.Field, atomic.Value)
@@ -218,7 +219,7 @@ func (p *Pipeline) toSI(ctx context.Context, atomic codec.Atomic, vehicleIntID i
218219
// them. The SettingChargeUnit signal is recorded for UI display
219220
// preference only.
220221
func needsConversion(field string, meta *protomodel.SignalMeta) bool {
221-
if isSpeedField(field) || units.IsFixedKiloToBaseField(field) {
222+
if isSpeedField(field) || units.IsFixedKiloToBaseField(field) || units.IsFixedMphSpeedField(field) {
222223
return true
223224
}
224225
if meta == nil {

internal/tesla/protomodel/coverage_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,39 @@ func TestCoverage_SettingUnitInvariants(t *testing.T) {
163163
// (e.g. a temperature reading) gets misclassified into a non-measurement
164164
// bucket and then gets silently dropped by the unit-history /
165165
// unit-conversion layer, which only iterates measurement-bucket fields.
166+
// TestCoverage_FleetTelemetryFields260to269 pins the firmware-2026.32 Field
167+
// enum values (teslamotors/fleet-telemetry@8fbaa100) on the vendored generator
168+
// output. The teslamotors Go module is still v0.9.0 (Go 1.25), so
169+
// TestCoverage_EveryProtoFieldHasSignalMeta cannot see these names via
170+
// ftproto.Field_name until that module pin moves.
171+
func TestCoverage_FleetTelemetryFields260to269(t *testing.T) {
172+
want := []struct {
173+
num int32
174+
name string
175+
}{
176+
{260, "GpsAccuracyMeters"},
177+
{261, "LifetimeEnergyChargedKwh"},
178+
{262, "BrickSocMinPercent"},
179+
{263, "NominalFullPackEnergyKwh"},
180+
{264, "GradeEstimatePercent"},
181+
{265, "MaxSpeedToReachDestinationMph"},
182+
{266, "SoftwareUpdateAvailable"},
183+
{267, "SoftwareUpdateInProgress"},
184+
{268, "RemoteStartActive"},
185+
{269, "SemiCruiseSpeedLimitMph"},
186+
}
187+
for _, w := range want {
188+
meta, ok := SignalsByEnum[w.num]
189+
if !ok {
190+
t.Errorf("SignalsByEnum missing enum=%d (%s); re-vendor proto and run go generate", w.num, w.name)
191+
continue
192+
}
193+
if meta.Field != w.name {
194+
t.Errorf("SignalsByEnum[%d].Field=%q want %q", w.num, meta.Field, w.name)
195+
}
196+
}
197+
}
198+
166199
func TestCoverage_UnitBearingFieldsAreMeasurements(t *testing.T) {
167200
denied := sortedKeys(nonMeasurementCategories)
168201

internal/tesla/protomodel/signal_metadata_gen.go

Lines changed: 60 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/tesla/router/routing.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ routes:
7777
dest: signal_log
7878
- field: BrickVoltageMin
7979
dest: signal_log
80+
- field: BrickSocMinPercent
81+
dest: signal_log
8082
- field: CabinOverheatProtectionMode
8183
dest: climate_snapshot
8284
column: cabin_overheat_protection_mode
@@ -435,6 +437,8 @@ routes:
435437
- field: Gear
436438
dest: drive_telemetry
437439
column: gear
440+
- field: GpsAccuracyMeters
441+
dest: signal_log
438442
- field: GpsHeading
439443
dest: positions
440444
column: heading_deg
@@ -506,6 +510,10 @@ routes:
506510
- field: LateralAcceleration
507511
dest: drive_telemetry
508512
column: lateral_acceleration_mps2
513+
- field: GradeEstimatePercent
514+
dest: signal_log
515+
- field: LifetimeEnergyChargedKwh
516+
dest: signal_log
509517
- field: LifetimeEnergyGainedRegen
510518
dest: drive_telemetry
511519
column: lifetime_energy_gained_regen_wh
@@ -602,6 +610,8 @@ routes:
602610
# to meters before the SI value lands in signal_log.
603611
- field: MilesSinceReset
604612
dest: signal_log
613+
- field: MaxSpeedToReachDestinationMph
614+
dest: signal_log
605615
- field: MilesToArrival
606616
dest: signal_log
607617
- field: MinutesToArrival
@@ -610,6 +620,8 @@ routes:
610620
dest: signal_log
611621
- field: ModuleTempMin
612622
dest: signal_log
623+
- field: NominalFullPackEnergyKwh
624+
dest: signal_log
613625
- field: NotEnoughPowerToHeat
614626
dest: signal_log
615627
- field: NumBrickVoltageMax
@@ -706,6 +718,8 @@ routes:
706718
# per-vehicle feature flag, and RoofColor is paint trim. Each
707719
# carries the change feed in signal_log; downstream readers fetch
708720
# the most recent value back from there. No hot column exists.
721+
- field: RemoteStartActive
722+
dest: signal_log
709723
- field: RemoteStartEnabled
710724
dest: signal_log
711725
- field: RightHandDrive
@@ -754,6 +768,8 @@ routes:
754768
# UnitKindDistance). Same routing rationale as MilesSinceReset —
755769
# the wire value is fixed in miles, normalize.toSI bypasses
756770
# vehicle_unit_history, and the SI value lands in signal_log.
771+
- field: SemiCruiseSpeedLimitMph
772+
dest: signal_log
757773
- field: SelfDrivingMilesSinceReset
758774
dest: signal_log
759775
- field: SemitruckPassengerSeatFoldPosition
@@ -840,10 +856,14 @@ routes:
840856
dest: signal_log
841857
# SoftwareUpdate* atomics (vehicle_state). Update-progress UI feeds
842858
# off the signal_log change stream; no hot column exists.
859+
- field: SoftwareUpdateAvailable
860+
dest: signal_log
843861
- field: SoftwareUpdateDownloadPercentComplete
844862
dest: signal_log
845863
- field: SoftwareUpdateExpectedDurationMinutes
846864
dest: signal_log
865+
- field: SoftwareUpdateInProgress
866+
dest: signal_log
847867
- field: SoftwareUpdateInstallationPercentComplete
848868
dest: signal_log
849869
- field: SoftwareUpdateScheduledStartTime

internal/tesla/units/conversions.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,14 +184,30 @@ func IsFixedMileDistanceField(field string) bool {
184184
// AC/DCChargingEnergyIn in kWh and AC/DCChargingPower in kW:
185185
// https://developer.tesla.com/docs/fleet-api/fleet-telemetry/available-data
186186
var fixedKiloToBaseFields = map[string]bool{
187-
"ACChargingEnergyIn": true,
188-
"DCChargingEnergyIn": true,
189-
"ACChargingPower": true,
190-
"DCChargingPower": true,
187+
"ACChargingEnergyIn": true,
188+
"DCChargingEnergyIn": true,
189+
"ACChargingPower": true,
190+
"DCChargingPower": true,
191+
"LifetimeEnergyChargedKwh": true,
192+
"NominalFullPackEnergyKwh": true,
191193
}
192194

193195
// IsFixedKiloToBaseField reports whether a Tesla field has a fixed kWh/kW
194196
// wire unit that must be converted to Wh/W without consulting unit history.
195197
func IsFixedKiloToBaseField(field string) bool {
196198
return fixedKiloToBaseFields[field]
197199
}
200+
201+
// fixedMphSpeedFields lists linear-velocity proto fields whose wire
202+
// value is ALWAYS miles per hour (name-encoded), independent of
203+
// SettingDistanceUnit. Canonical SI is metres per second.
204+
var fixedMphSpeedFields = map[string]bool{
205+
"MaxSpeedToReachDestinationMph": true,
206+
"SemiCruiseSpeedLimitMph": true,
207+
}
208+
209+
// IsFixedMphSpeedField reports whether a Tesla speed field is always
210+
// mph on the wire and must convert to m/s without unit history.
211+
func IsFixedMphSpeedField(field string) bool {
212+
return fixedMphSpeedFields[field]
213+
}

0 commit comments

Comments
 (0)