diff --git a/.changeset/model-dashcam-sei-metadata.md b/.changeset/model-dashcam-sei-metadata.md new file mode 100644 index 0000000..d8400e0 --- /dev/null +++ b/.changeset/model-dashcam-sei-metadata.md @@ -0,0 +1,5 @@ +--- +"@teslemetry/tesla-protocol": minor +--- + +Model `DashcamSei.SeiMetadata`, the per-frame vehicle telemetry (speed, gear, steering, GPS, heading, linear acceleration, autopilot state, blinkers, brake) carried in the SEI payload embedded in TeslaCam/Sentry/live-camera video - based on our own observations and contributions from the community. diff --git a/AGENTS.md b/AGENTS.md index f06a138..d335962 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,6 +11,7 @@ This file is the project's committed home for project-intrinsic agent knowledge: - Un-reserving a field to model it (as field 12 above did) trips buf's `RESERVED_MESSAGE_NO_DELETE` breaking check even though it's a wire-compatible addition; `buf.yaml` excepts that rule repo-wide for this reason, so the same move for fields 17/19 later won't need a fresh exception. - The committed `proto/command/` baseline already reflects one specific Tesla app build reconstructed via cross-version protobuf diffing (`app-4.58.6`, see `TESLEMETRY-EXT` markers). A later app-version-diff pass will find most candidate fields already modelled here - check the current `.proto` files for the exact field/tag before assuming it needs adding, and only model the genuine delta. - `proto/charging/` is a documented exception to the "app-only namespaces are not published" policy (`PROVENANCE.md`): it has no BLE or Fleet API wire path today, but was published because it describes the vehicle-charging product domain. Any future app-only module wanting the same exception should be argued explicitly, not assumed from this precedent. +- Adding a wholly new proto directory (a source with no upstream) needs registering in three places: the `buf.yaml` module list, `GROUPS_LIST` in `scripts/generate.sh`, and the layout table + provenance table in `README.md`/`PROVENANCE.md`. TypeScript/Python package exports are wildcarded, so no per-group registration is needed there. ## Maintaining this file diff --git a/PROVENANCE.md b/PROVENANCE.md index 7a70002..bff9149 100644 --- a/PROVENANCE.md +++ b/PROVENANCE.md @@ -15,6 +15,7 @@ directories no public upstream exists and this repo is the source of record. | `proto/energy_device/`, `proto/energy_command/` | Teslemetry source-of-record | no upstream (TEG gateway local API, from our own observations and contributions from the community) | | `proto/teslapower/` | Teslemetry source-of-record | no upstream (Powerwall local API, from our own observations and contributions from the community, credit @brianhealey) | | `proto/charging/` | Teslemetry source-of-record | no upstream (EV charge-session energy metering / billing schema, from our own observations and contributions from the community) | +| `proto/dashcam/` | Teslemetry source-of-record | no upstream (TeslaCam/Sentry video SEI metadata, from our own observations and contributions from the community) | ## How the boundary is kept visible diff --git a/README.md b/README.md index b2d4814..e28f834 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,8 @@ proto/ source of truth (the only hand-edited artifacts) ├── energy_device/ TEG gateway local-API protos ├── energy_command/ energy command identifiers ├── teslapower/ Powerwall local-API proto -└── charging/ EV charge-session metering/billing ("STEM") protos +├── charging/ EV charge-session metering/billing ("STEM") protos +└── dashcam/ TeslaCam/Sentry video SEI metadata proto packages/typescript/ @teslemetry/tesla-protocol (generated, committed) packages/python/ tesla-protocol (generated, committed) ``` diff --git a/buf.yaml b/buf.yaml index c6240e4..d21d8ef 100644 --- a/buf.yaml +++ b/buf.yaml @@ -6,6 +6,7 @@ modules: - path: proto/energy_command - path: proto/teslapower - path: proto/charging + - path: proto/dashcam lint: # Tesla's upstream style (package CarServer, enum value SpeedLimit, file-level # layout) violates buf's naming rules; we inherit their style, we don't fix it. diff --git a/packages/python/tesla_protocol/dashcam/__init__.py b/packages/python/tesla_protocol/dashcam/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/packages/python/tesla_protocol/dashcam/__init__.pyi b/packages/python/tesla_protocol/dashcam/__init__.pyi new file mode 100644 index 0000000..621ef21 --- /dev/null +++ b/packages/python/tesla_protocol/dashcam/__init__.pyi @@ -0,0 +1 @@ +from . import dashcam_sei_pb2 diff --git a/packages/python/tesla_protocol/dashcam/dashcam_sei_pb2.py b/packages/python/tesla_protocol/dashcam/dashcam_sei_pb2.py new file mode 100644 index 0000000..5d9a948 --- /dev/null +++ b/packages/python/tesla_protocol/dashcam/dashcam_sei_pb2.py @@ -0,0 +1,20 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 6, 31, 1, '', 'dashcam_sei.proto') +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11dashcam_sei.proto\x12\nDashcamSei"\xf3\x04\n\x0bSeiMetadata\x12\x0f\n\x07field_1\x18\x01 \x01(\r\x12/\n\tgearState\x18\x02 \x01(\x0e2\x1c.DashcamSei.SeiMetadata.Gear\x12\x12\n\nframeSeqNo\x18\x03 \x01(\x04\x12\x17\n\x0fvehicleSpeedMps\x18\x04 \x01(\x02\x12 \n\x18acceleratorPedalPosition\x18\x05 \x01(\x02\x12\x1a\n\x12steeringWheelAngle\x18\x06 \x01(\x02\x12\x15\n\rblinkerOnLeft\x18\x07 \x01(\x08\x12\x16\n\x0eblinkerOnRight\x18\x08 \x01(\x08\x12\x14\n\x0cbrakeApplied\x18\t \x01(\x08\x12>\n\x0eautopilotState\x18\n \x01(\x0e2&.DashcamSei.SeiMetadata.AutopilotState\x12\x13\n\x0blatitudeDeg\x18\x0b \x01(\x01\x12\x14\n\x0clongitudeDeg\x18\x0c \x01(\x01\x12\x12\n\nheadingDeg\x18\r \x01(\x01\x12\x1f\n\x17linearAccelerationMps2X\x18\x0e \x01(\x01\x12\x1f\n\x17linearAccelerationMps2Y\x18\x0f \x01(\x01\x12\x1f\n\x17linearAccelerationMps2Z\x18\x10 \x01(\x01"I\n\x04Gear\x12\r\n\tGEAR_PARK\x10\x00\x12\x0e\n\nGEAR_DRIVE\x10\x01\x12\x10\n\x0cGEAR_REVERSE\x10\x02\x12\x10\n\x0cGEAR_NEUTRAL\x10\x03"E\n\x0eAutopilotState\x12\x08\n\x04NONE\x10\x00\x12\x10\n\x0cSELF_DRIVING\x10\x01\x12\r\n\tAUTOSTEER\x10\x02\x12\x08\n\x04TACC\x10\x03b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'dashcam_sei_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_SEIMETADATA']._serialized_start = 34 + _globals['_SEIMETADATA']._serialized_end = 661 + _globals['_SEIMETADATA_GEAR']._serialized_start = 517 + _globals['_SEIMETADATA_GEAR']._serialized_end = 590 + _globals['_SEIMETADATA_AUTOPILOTSTATE']._serialized_start = 592 + _globals['_SEIMETADATA_AUTOPILOTSTATE']._serialized_end = 661 \ No newline at end of file diff --git a/packages/python/tesla_protocol/dashcam/dashcam_sei_pb2.pyi b/packages/python/tesla_protocol/dashcam/dashcam_sei_pb2.pyi new file mode 100644 index 0000000..54e0157 --- /dev/null +++ b/packages/python/tesla_protocol/dashcam/dashcam_sei_pb2.pyi @@ -0,0 +1,65 @@ +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union +DESCRIPTOR: _descriptor.FileDescriptor + +class SeiMetadata(_message.Message): + __slots__ = ('field_1', 'gearState', 'frameSeqNo', 'vehicleSpeedMps', 'acceleratorPedalPosition', 'steeringWheelAngle', 'blinkerOnLeft', 'blinkerOnRight', 'brakeApplied', 'autopilotState', 'latitudeDeg', 'longitudeDeg', 'headingDeg', 'linearAccelerationMps2X', 'linearAccelerationMps2Y', 'linearAccelerationMps2Z') + + class Gear(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + GEAR_PARK: _ClassVar[SeiMetadata.Gear] + GEAR_DRIVE: _ClassVar[SeiMetadata.Gear] + GEAR_REVERSE: _ClassVar[SeiMetadata.Gear] + GEAR_NEUTRAL: _ClassVar[SeiMetadata.Gear] + GEAR_PARK: SeiMetadata.Gear + GEAR_DRIVE: SeiMetadata.Gear + GEAR_REVERSE: SeiMetadata.Gear + GEAR_NEUTRAL: SeiMetadata.Gear + + class AutopilotState(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + NONE: _ClassVar[SeiMetadata.AutopilotState] + SELF_DRIVING: _ClassVar[SeiMetadata.AutopilotState] + AUTOSTEER: _ClassVar[SeiMetadata.AutopilotState] + TACC: _ClassVar[SeiMetadata.AutopilotState] + NONE: SeiMetadata.AutopilotState + SELF_DRIVING: SeiMetadata.AutopilotState + AUTOSTEER: SeiMetadata.AutopilotState + TACC: SeiMetadata.AutopilotState + FIELD_1_FIELD_NUMBER: _ClassVar[int] + GEARSTATE_FIELD_NUMBER: _ClassVar[int] + FRAMESEQNO_FIELD_NUMBER: _ClassVar[int] + VEHICLESPEEDMPS_FIELD_NUMBER: _ClassVar[int] + ACCELERATORPEDALPOSITION_FIELD_NUMBER: _ClassVar[int] + STEERINGWHEELANGLE_FIELD_NUMBER: _ClassVar[int] + BLINKERONLEFT_FIELD_NUMBER: _ClassVar[int] + BLINKERONRIGHT_FIELD_NUMBER: _ClassVar[int] + BRAKEAPPLIED_FIELD_NUMBER: _ClassVar[int] + AUTOPILOTSTATE_FIELD_NUMBER: _ClassVar[int] + LATITUDEDEG_FIELD_NUMBER: _ClassVar[int] + LONGITUDEDEG_FIELD_NUMBER: _ClassVar[int] + HEADINGDEG_FIELD_NUMBER: _ClassVar[int] + LINEARACCELERATIONMPS2X_FIELD_NUMBER: _ClassVar[int] + LINEARACCELERATIONMPS2Y_FIELD_NUMBER: _ClassVar[int] + LINEARACCELERATIONMPS2Z_FIELD_NUMBER: _ClassVar[int] + field_1: int + gearState: SeiMetadata.Gear + frameSeqNo: int + vehicleSpeedMps: float + acceleratorPedalPosition: float + steeringWheelAngle: float + blinkerOnLeft: bool + blinkerOnRight: bool + brakeApplied: bool + autopilotState: SeiMetadata.AutopilotState + latitudeDeg: float + longitudeDeg: float + headingDeg: float + linearAccelerationMps2X: float + linearAccelerationMps2Y: float + linearAccelerationMps2Z: float + + def __init__(self, field_1: _Optional[int]=..., gearState: _Optional[_Union[SeiMetadata.Gear, str]]=..., frameSeqNo: _Optional[int]=..., vehicleSpeedMps: _Optional[float]=..., acceleratorPedalPosition: _Optional[float]=..., steeringWheelAngle: _Optional[float]=..., blinkerOnLeft: bool=..., blinkerOnRight: bool=..., brakeApplied: bool=..., autopilotState: _Optional[_Union[SeiMetadata.AutopilotState, str]]=..., latitudeDeg: _Optional[float]=..., longitudeDeg: _Optional[float]=..., headingDeg: _Optional[float]=..., linearAccelerationMps2X: _Optional[float]=..., linearAccelerationMps2Y: _Optional[float]=..., linearAccelerationMps2Z: _Optional[float]=...) -> None: + ... \ No newline at end of file diff --git a/packages/typescript/src/dashcam/dashcam_sei.ts b/packages/typescript/src/dashcam/dashcam_sei.ts new file mode 100644 index 0000000..7cc2ca3 --- /dev/null +++ b/packages/typescript/src/dashcam/dashcam_sei.ts @@ -0,0 +1,486 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v2.7.7 +// protoc v6.31.1 +// source: dashcam_sei.proto + +/* eslint-disable */ +import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; + +export const protobufPackage = "DashcamSei"; + +/** + * SeiMetadata is the per-frame vehicle telemetry carried in the SEI (supplemental + * enhancement information) payload embedded in TeslaCam / Sentry / live-camera + * H.264 video, synchronized to the frame it accompanies. + */ +export interface SeiMetadata { + /** Schema/frame-type version byte; meaning beyond that is not confirmed. */ + field1: number; + gearState: SeiMetadata_Gear; + frameSeqNo: number; + vehicleSpeedMps: number; + acceleratorPedalPosition: number; + steeringWheelAngle: number; + blinkerOnLeft: boolean; + blinkerOnRight: boolean; + brakeApplied: boolean; + autopilotState: SeiMetadata_AutopilotState; + latitudeDeg: number; + longitudeDeg: number; + headingDeg: number; + linearAccelerationMps2X: number; + linearAccelerationMps2Y: number; + linearAccelerationMps2Z: number; +} + +export enum SeiMetadata_Gear { + GEAR_PARK = 0, + GEAR_DRIVE = 1, + GEAR_REVERSE = 2, + GEAR_NEUTRAL = 3, + UNRECOGNIZED = -1, +} + +export function seiMetadata_GearFromJSON(object: any): SeiMetadata_Gear { + switch (object) { + case 0: + case "GEAR_PARK": + return SeiMetadata_Gear.GEAR_PARK; + case 1: + case "GEAR_DRIVE": + return SeiMetadata_Gear.GEAR_DRIVE; + case 2: + case "GEAR_REVERSE": + return SeiMetadata_Gear.GEAR_REVERSE; + case 3: + case "GEAR_NEUTRAL": + return SeiMetadata_Gear.GEAR_NEUTRAL; + case -1: + case "UNRECOGNIZED": + default: + return SeiMetadata_Gear.UNRECOGNIZED; + } +} + +export function seiMetadata_GearToJSON(object: SeiMetadata_Gear): string { + switch (object) { + case SeiMetadata_Gear.GEAR_PARK: + return "GEAR_PARK"; + case SeiMetadata_Gear.GEAR_DRIVE: + return "GEAR_DRIVE"; + case SeiMetadata_Gear.GEAR_REVERSE: + return "GEAR_REVERSE"; + case SeiMetadata_Gear.GEAR_NEUTRAL: + return "GEAR_NEUTRAL"; + case SeiMetadata_Gear.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +export enum SeiMetadata_AutopilotState { + NONE = 0, + SELF_DRIVING = 1, + AUTOSTEER = 2, + TACC = 3, + UNRECOGNIZED = -1, +} + +export function seiMetadata_AutopilotStateFromJSON(object: any): SeiMetadata_AutopilotState { + switch (object) { + case 0: + case "NONE": + return SeiMetadata_AutopilotState.NONE; + case 1: + case "SELF_DRIVING": + return SeiMetadata_AutopilotState.SELF_DRIVING; + case 2: + case "AUTOSTEER": + return SeiMetadata_AutopilotState.AUTOSTEER; + case 3: + case "TACC": + return SeiMetadata_AutopilotState.TACC; + case -1: + case "UNRECOGNIZED": + default: + return SeiMetadata_AutopilotState.UNRECOGNIZED; + } +} + +export function seiMetadata_AutopilotStateToJSON(object: SeiMetadata_AutopilotState): string { + switch (object) { + case SeiMetadata_AutopilotState.NONE: + return "NONE"; + case SeiMetadata_AutopilotState.SELF_DRIVING: + return "SELF_DRIVING"; + case SeiMetadata_AutopilotState.AUTOSTEER: + return "AUTOSTEER"; + case SeiMetadata_AutopilotState.TACC: + return "TACC"; + case SeiMetadata_AutopilotState.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +function createBaseSeiMetadata(): SeiMetadata { + return { + field1: 0, + gearState: 0, + frameSeqNo: 0, + vehicleSpeedMps: 0, + acceleratorPedalPosition: 0, + steeringWheelAngle: 0, + blinkerOnLeft: false, + blinkerOnRight: false, + brakeApplied: false, + autopilotState: 0, + latitudeDeg: 0, + longitudeDeg: 0, + headingDeg: 0, + linearAccelerationMps2X: 0, + linearAccelerationMps2Y: 0, + linearAccelerationMps2Z: 0, + }; +} + +export const SeiMetadata: MessageFns = { + encode(message: SeiMetadata, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.field1 !== 0) { + writer.uint32(8).uint32(message.field1); + } + if (message.gearState !== 0) { + writer.uint32(16).int32(message.gearState); + } + if (message.frameSeqNo !== 0) { + writer.uint32(24).uint64(message.frameSeqNo); + } + if (message.vehicleSpeedMps !== 0) { + writer.uint32(37).float(message.vehicleSpeedMps); + } + if (message.acceleratorPedalPosition !== 0) { + writer.uint32(45).float(message.acceleratorPedalPosition); + } + if (message.steeringWheelAngle !== 0) { + writer.uint32(53).float(message.steeringWheelAngle); + } + if (message.blinkerOnLeft !== false) { + writer.uint32(56).bool(message.blinkerOnLeft); + } + if (message.blinkerOnRight !== false) { + writer.uint32(64).bool(message.blinkerOnRight); + } + if (message.brakeApplied !== false) { + writer.uint32(72).bool(message.brakeApplied); + } + if (message.autopilotState !== 0) { + writer.uint32(80).int32(message.autopilotState); + } + if (message.latitudeDeg !== 0) { + writer.uint32(89).double(message.latitudeDeg); + } + if (message.longitudeDeg !== 0) { + writer.uint32(97).double(message.longitudeDeg); + } + if (message.headingDeg !== 0) { + writer.uint32(105).double(message.headingDeg); + } + if (message.linearAccelerationMps2X !== 0) { + writer.uint32(113).double(message.linearAccelerationMps2X); + } + if (message.linearAccelerationMps2Y !== 0) { + writer.uint32(121).double(message.linearAccelerationMps2Y); + } + if (message.linearAccelerationMps2Z !== 0) { + writer.uint32(129).double(message.linearAccelerationMps2Z); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): SeiMetadata { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSeiMetadata(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.field1 = reader.uint32(); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.gearState = reader.int32() as any; + continue; + } + case 3: { + if (tag !== 24) { + break; + } + + message.frameSeqNo = longToNumber(reader.uint64()); + continue; + } + case 4: { + if (tag !== 37) { + break; + } + + message.vehicleSpeedMps = reader.float(); + continue; + } + case 5: { + if (tag !== 45) { + break; + } + + message.acceleratorPedalPosition = reader.float(); + continue; + } + case 6: { + if (tag !== 53) { + break; + } + + message.steeringWheelAngle = reader.float(); + continue; + } + case 7: { + if (tag !== 56) { + break; + } + + message.blinkerOnLeft = reader.bool(); + continue; + } + case 8: { + if (tag !== 64) { + break; + } + + message.blinkerOnRight = reader.bool(); + continue; + } + case 9: { + if (tag !== 72) { + break; + } + + message.brakeApplied = reader.bool(); + continue; + } + case 10: { + if (tag !== 80) { + break; + } + + message.autopilotState = reader.int32() as any; + continue; + } + case 11: { + if (tag !== 89) { + break; + } + + message.latitudeDeg = reader.double(); + continue; + } + case 12: { + if (tag !== 97) { + break; + } + + message.longitudeDeg = reader.double(); + continue; + } + case 13: { + if (tag !== 105) { + break; + } + + message.headingDeg = reader.double(); + continue; + } + case 14: { + if (tag !== 113) { + break; + } + + message.linearAccelerationMps2X = reader.double(); + continue; + } + case 15: { + if (tag !== 121) { + break; + } + + message.linearAccelerationMps2Y = reader.double(); + continue; + } + case 16: { + if (tag !== 129) { + break; + } + + message.linearAccelerationMps2Z = reader.double(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): SeiMetadata { + return { + field1: isSet(object.field1) ? globalThis.Number(object.field1) : 0, + gearState: isSet(object.gearState) ? seiMetadata_GearFromJSON(object.gearState) : 0, + frameSeqNo: isSet(object.frameSeqNo) ? globalThis.Number(object.frameSeqNo) : 0, + vehicleSpeedMps: isSet(object.vehicleSpeedMps) ? globalThis.Number(object.vehicleSpeedMps) : 0, + acceleratorPedalPosition: isSet(object.acceleratorPedalPosition) + ? globalThis.Number(object.acceleratorPedalPosition) + : 0, + steeringWheelAngle: isSet(object.steeringWheelAngle) ? globalThis.Number(object.steeringWheelAngle) : 0, + blinkerOnLeft: isSet(object.blinkerOnLeft) ? globalThis.Boolean(object.blinkerOnLeft) : false, + blinkerOnRight: isSet(object.blinkerOnRight) ? globalThis.Boolean(object.blinkerOnRight) : false, + brakeApplied: isSet(object.brakeApplied) ? globalThis.Boolean(object.brakeApplied) : false, + autopilotState: isSet(object.autopilotState) ? seiMetadata_AutopilotStateFromJSON(object.autopilotState) : 0, + latitudeDeg: isSet(object.latitudeDeg) ? globalThis.Number(object.latitudeDeg) : 0, + longitudeDeg: isSet(object.longitudeDeg) ? globalThis.Number(object.longitudeDeg) : 0, + headingDeg: isSet(object.headingDeg) ? globalThis.Number(object.headingDeg) : 0, + linearAccelerationMps2X: isSet(object.linearAccelerationMps2X) + ? globalThis.Number(object.linearAccelerationMps2X) + : 0, + linearAccelerationMps2Y: isSet(object.linearAccelerationMps2Y) + ? globalThis.Number(object.linearAccelerationMps2Y) + : 0, + linearAccelerationMps2Z: isSet(object.linearAccelerationMps2Z) + ? globalThis.Number(object.linearAccelerationMps2Z) + : 0, + }; + }, + + toJSON(message: SeiMetadata): unknown { + const obj: any = {}; + if (message.field1 !== undefined) { + obj.field1 = Math.round(message.field1); + } + if (message.gearState !== undefined) { + obj.gearState = seiMetadata_GearToJSON(message.gearState); + } + if (message.frameSeqNo !== undefined) { + obj.frameSeqNo = Math.round(message.frameSeqNo); + } + if (message.vehicleSpeedMps !== undefined) { + obj.vehicleSpeedMps = message.vehicleSpeedMps; + } + if (message.acceleratorPedalPosition !== undefined) { + obj.acceleratorPedalPosition = message.acceleratorPedalPosition; + } + if (message.steeringWheelAngle !== undefined) { + obj.steeringWheelAngle = message.steeringWheelAngle; + } + if (message.blinkerOnLeft !== undefined) { + obj.blinkerOnLeft = message.blinkerOnLeft; + } + if (message.blinkerOnRight !== undefined) { + obj.blinkerOnRight = message.blinkerOnRight; + } + if (message.brakeApplied !== undefined) { + obj.brakeApplied = message.brakeApplied; + } + if (message.autopilotState !== undefined) { + obj.autopilotState = seiMetadata_AutopilotStateToJSON(message.autopilotState); + } + if (message.latitudeDeg !== undefined) { + obj.latitudeDeg = message.latitudeDeg; + } + if (message.longitudeDeg !== undefined) { + obj.longitudeDeg = message.longitudeDeg; + } + if (message.headingDeg !== undefined) { + obj.headingDeg = message.headingDeg; + } + if (message.linearAccelerationMps2X !== undefined) { + obj.linearAccelerationMps2X = message.linearAccelerationMps2X; + } + if (message.linearAccelerationMps2Y !== undefined) { + obj.linearAccelerationMps2Y = message.linearAccelerationMps2Y; + } + if (message.linearAccelerationMps2Z !== undefined) { + obj.linearAccelerationMps2Z = message.linearAccelerationMps2Z; + } + return obj; + }, + + create, I>>(base?: I): SeiMetadata { + return SeiMetadata.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): SeiMetadata { + const message = createBaseSeiMetadata(); + message.field1 = object.field1 ?? 0; + message.gearState = object.gearState ?? 0; + message.frameSeqNo = object.frameSeqNo ?? 0; + message.vehicleSpeedMps = object.vehicleSpeedMps ?? 0; + message.acceleratorPedalPosition = object.acceleratorPedalPosition ?? 0; + message.steeringWheelAngle = object.steeringWheelAngle ?? 0; + message.blinkerOnLeft = object.blinkerOnLeft ?? false; + message.blinkerOnRight = object.blinkerOnRight ?? false; + message.brakeApplied = object.brakeApplied ?? false; + message.autopilotState = object.autopilotState ?? 0; + message.latitudeDeg = object.latitudeDeg ?? 0; + message.longitudeDeg = object.longitudeDeg ?? 0; + message.headingDeg = object.headingDeg ?? 0; + message.linearAccelerationMps2X = object.linearAccelerationMps2X ?? 0; + message.linearAccelerationMps2Y = object.linearAccelerationMps2Y ?? 0; + message.linearAccelerationMps2Z = object.linearAccelerationMps2Z ?? 0; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function longToNumber(int64: { toString(): string }): number { + const num = globalThis.Number(int64.toString()); + if (num > globalThis.Number.MAX_SAFE_INTEGER) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + if (num < globalThis.Number.MIN_SAFE_INTEGER) { + throw new globalThis.Error("Value is smaller than Number.MIN_SAFE_INTEGER"); + } + return num; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export interface MessageFns { + encode(message: T, writer?: BinaryWriter): BinaryWriter; + decode(input: BinaryReader | Uint8Array, length?: number): T; + fromJSON(object: any): T; + toJSON(message: T): unknown; + create, I>>(base?: I): T; + fromPartial, I>>(object: I): T; +} diff --git a/proto/dashcam/dashcam_sei.proto b/proto/dashcam/dashcam_sei.proto new file mode 100644 index 0000000..3b68f77 --- /dev/null +++ b/proto/dashcam/dashcam_sei.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; + +package DashcamSei; + +// SeiMetadata is the per-frame vehicle telemetry carried in the SEI (supplemental +// enhancement information) payload embedded in TeslaCam / Sentry / live-camera +// H.264 video, synchronized to the frame it accompanies. +message SeiMetadata { + enum Gear { + GEAR_PARK = 0; + GEAR_DRIVE = 1; + GEAR_REVERSE = 2; + GEAR_NEUTRAL = 3; + } + + enum AutopilotState { + NONE = 0; + SELF_DRIVING = 1; + AUTOSTEER = 2; + TACC = 3; + } + + // Schema/frame-type version byte; meaning beyond that is not confirmed. + uint32 field_1 = 1; + Gear gearState = 2; + uint64 frameSeqNo = 3; + float vehicleSpeedMps = 4; + float acceleratorPedalPosition = 5; + float steeringWheelAngle = 6; + bool blinkerOnLeft = 7; + bool blinkerOnRight = 8; + bool brakeApplied = 9; + AutopilotState autopilotState = 10; + double latitudeDeg = 11; + double longitudeDeg = 12; + double headingDeg = 13; + double linearAccelerationMps2X = 14; + double linearAccelerationMps2Y = 15; + double linearAccelerationMps2Z = 16; +} diff --git a/scripts/generate.sh b/scripts/generate.sh index 55f627e..861e51a 100755 --- a/scripts/generate.sh +++ b/scripts/generate.sh @@ -23,7 +23,7 @@ TS_OPTS="emitDefaultValues=json-methods,importSuffix=.js" TS_OUT=packages/typescript/src PY_PKG=packages/python/tesla_protocol -GROUPS_LIST="command telemetry energy_device energy_command teslapower charging" +GROUPS_LIST="command telemetry energy_device energy_command teslapower charging dashcam" rm -rf "$TS_OUT" for group in $GROUPS_LIST; do