diff --git a/AGENTS.md b/AGENTS.md index 5b83564..116e830 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -95,7 +95,7 @@ All exceptions inherit from `TeslaFleetError(BaseException)`. ### Protobuf -Generated protobuf files live in `tesla/vehicle/proto/` and are excluded from ruff and pyright. Do not edit these directly — regenerate them with `tools/regenerate_protos.sh` (needs `protoc` on `PATH`). +Source protobuf definitions live in `proto/`; generated Python files live in `tesla_fleet_api/tesla/vehicle/proto/` and are excluded from ruff and pyright. Do not edit generated `*_pb2.py`/`*_pb2.pyi` files directly — update the source `.proto`, add new files to the `PROTOS` list and import-rewrite set in `tools/regenerate_protos.sh`, then regenerate (needs `protoc` on `PATH`). If a new generated module is added, also update the hand-maintained `tesla_fleet_api/tesla/vehicle/proto/__init__.pyi` export stub. **Runtime-version pin (Home Assistant compatibility).** The gencode stamps a `ValidateProtobufRuntimeVersion(major, minor, patch, …)` call, and protobuf refuses to load gencode that is *newer* than the installed runtime (`gencode X > runtime` → `VersionError`). Home Assistant core pins `protobuf==6.32.0`, so the gencode must be stamped **≤ 6.32.0** or it breaks in HA. The generator version is the `protoc` version: under unified protobuf versioning, `protoc vX.Y` (`libprotoc X.Y`) stamps Python gencode `6.X.Y`. So to target runtime 6.32.0, regenerate with **protoc v32.0** (`protoc-32.0-linux-x86_64.zip` from the protobuf GitHub releases). The `protobuf>=6.32.0` floor in `pyproject.toml` must match the gencode version — never set it below the stamped version, or installs that resolve an older protobuf will hit `VersionError` at import. diff --git a/proto/session.proto b/proto/session.proto new file mode 100644 index 0000000..0c73d65 --- /dev/null +++ b/proto/session.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; +import "google/protobuf/timestamp.proto"; + +package Session; + +message SessionStore { + bytes public_key = 1; + bytes epoch = 2; + uint32 delta = 3; + optional google.protobuf.Timestamp lock = 4; +} diff --git a/proto/universal_message.proto b/proto/universal_message.proto index d521b52..fddee87 100644 --- a/proto/universal_message.proto +++ b/proto/universal_message.proto @@ -11,6 +11,9 @@ enum Domain { DOMAIN_BROADCAST = 0; DOMAIN_VEHICLE_SECURITY = 2; DOMAIN_INFOTAINMENT = 3; + DOMAIN_AUTHD = 5; + DOMAIN_ENERGY_DEVICE = 7; + DOMAIN_ENERGY_DEVICE_AUTH = 8; } message Destination { diff --git a/tesla_fleet_api/tesla/vehicle/proto/__init__.pyi b/tesla_fleet_api/tesla/vehicle/proto/__init__.pyi index a3ecae6..324679c 100644 --- a/tesla_fleet_api/tesla/vehicle/proto/__init__.pyi +++ b/tesla_fleet_api/tesla/vehicle/proto/__init__.pyi @@ -3,6 +3,7 @@ from . import common_pb2 from . import errors_pb2 from . import keys_pb2 from . import managed_charging_pb2 +from . import session_pb2 from . import signatures_pb2 from . import universal_message_pb2 from . import vcsec_pb2 diff --git a/tesla_fleet_api/tesla/vehicle/proto/session_pb2.py b/tesla_fleet_api/tesla/vehicle/proto/session_pb2.py new file mode 100644 index 0000000..86c9cb0 --- /dev/null +++ b/tesla_fleet_api/tesla/vehicle/proto/session_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: tesla/session.proto +# Protobuf Python Version: 6.32.0 +"""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, + 32, + 0, + '', + 'tesla/session.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13tesla/session.proto\x12\x07Session\x1a\x1fgoogle/protobuf/timestamp.proto\"x\n\x0cSessionStore\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\x12\r\n\x05\x65poch\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lta\x18\x03 \x01(\r\x12-\n\x04lock\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x00\x88\x01\x01\x42\x07\n\x05_lockb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'tesla.session_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_SESSIONSTORE']._serialized_start=65 + _globals['_SESSIONSTORE']._serialized_end=185 +# @@protoc_insertion_point(module_scope) diff --git a/tesla_fleet_api/tesla/vehicle/proto/session_pb2.pyi b/tesla_fleet_api/tesla/vehicle/proto/session_pb2.pyi new file mode 100644 index 0000000..a048824 --- /dev/null +++ b/tesla_fleet_api/tesla/vehicle/proto/session_pb2.pyi @@ -0,0 +1,21 @@ +import datetime + +from google.protobuf import timestamp_pb2 as _timestamp_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class SessionStore(_message.Message): + __slots__ = ("public_key", "epoch", "delta", "lock") + PUBLIC_KEY_FIELD_NUMBER: _ClassVar[int] + EPOCH_FIELD_NUMBER: _ClassVar[int] + DELTA_FIELD_NUMBER: _ClassVar[int] + LOCK_FIELD_NUMBER: _ClassVar[int] + public_key: bytes + epoch: bytes + delta: int + lock: _timestamp_pb2.Timestamp + def __init__(self, public_key: _Optional[bytes] = ..., epoch: _Optional[bytes] = ..., delta: _Optional[int] = ..., lock: _Optional[_Union[datetime.datetime, _timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... diff --git a/tesla_fleet_api/tesla/vehicle/proto/universal_message_pb2.py b/tesla_fleet_api/tesla/vehicle/proto/universal_message_pb2.py index a6bfd78..879c1fc 100644 --- a/tesla_fleet_api/tesla/vehicle/proto/universal_message_pb2.py +++ b/tesla_fleet_api/tesla/vehicle/proto/universal_message_pb2.py @@ -25,7 +25,7 @@ from . import signatures_pb2 as tesla_dot_signatures__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1dtesla/universal_message.proto\x12\x10UniversalMessage\x1a\x16tesla/signatures.proto\"g\n\x0b\x44\x65stination\x12*\n\x06\x64omain\x18\x01 \x01(\x0e\x32\x18.UniversalMessage.DomainH\x00\x12\x19\n\x0frouting_address\x18\x02 \x01(\x0cH\x00\x42\x11\n\x0fsub_destination\"\x8e\x01\n\rMessageStatus\x12=\n\x10operation_status\x18\x01 \x01(\x0e\x32#.UniversalMessage.OperationStatus_E\x12>\n\x14signed_message_fault\x18\x02 \x01(\x0e\x32 .UniversalMessage.MessageFault_E\";\n\x12SessionInfoRequest\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\x12\x11\n\tchallenge\x18\x02 \x01(\x0c\"\xd6\x03\n\x0fRoutableMessage\x12\x35\n\x0eto_destination\x18\x06 \x01(\x0b\x32\x1d.UniversalMessage.Destination\x12\x37\n\x10\x66rom_destination\x18\x07 \x01(\x0b\x32\x1d.UniversalMessage.Destination\x12#\n\x19protobuf_message_as_bytes\x18\n \x01(\x0cH\x00\x12\x44\n\x14session_info_request\x18\x0e \x01(\x0b\x32$.UniversalMessage.SessionInfoRequestH\x00\x12\x16\n\x0csession_info\x18\x0f \x01(\x0cH\x00\x12\x33\n\x0esignature_data\x18\r \x01(\x0b\x32\x19.Signatures.SignatureDataH\x01\x12<\n\x13signedMessageStatus\x18\x0c \x01(\x0b\x32\x1f.UniversalMessage.MessageStatus\x12\x14\n\x0crequest_uuid\x18\x32 \x01(\x0c\x12\x0c\n\x04uuid\x18\x33 \x01(\x0c\x12\r\n\x05\x66lags\x18\x34 \x01(\rB\t\n\x07payloadB\r\n\x0bsub_sigDataJ\x04\x08\x01\x10\x06J\x04\x08\x10\x10)J\x04\x08\x0b\x10\x0c*T\n\x06\x44omain\x12\x14\n\x10\x44OMAIN_BROADCAST\x10\x00\x12\x1b\n\x17\x44OMAIN_VEHICLE_SECURITY\x10\x02\x12\x17\n\x13\x44OMAIN_INFOTAINMENT\x10\x03*`\n\x11OperationStatus_E\x12\x16\n\x12OPERATIONSTATUS_OK\x10\x00\x12\x18\n\x14OPERATIONSTATUS_WAIT\x10\x01\x12\x19\n\x15OPERATIONSTATUS_ERROR\x10\x02*\xe4\t\n\x0eMessageFault_E\x12\x1b\n\x17MESSAGEFAULT_ERROR_NONE\x10\x00\x12\x1b\n\x17MESSAGEFAULT_ERROR_BUSY\x10\x01\x12\x1e\n\x1aMESSAGEFAULT_ERROR_TIMEOUT\x10\x02\x12%\n!MESSAGEFAULT_ERROR_UNKNOWN_KEY_ID\x10\x03\x12#\n\x1fMESSAGEFAULT_ERROR_INACTIVE_KEY\x10\x04\x12(\n$MESSAGEFAULT_ERROR_INVALID_SIGNATURE\x10\x05\x12/\n+MESSAGEFAULT_ERROR_INVALID_TOKEN_OR_COUNTER\x10\x06\x12.\n*MESSAGEFAULT_ERROR_INSUFFICIENT_PRIVILEGES\x10\x07\x12&\n\"MESSAGEFAULT_ERROR_INVALID_DOMAINS\x10\x08\x12&\n\"MESSAGEFAULT_ERROR_INVALID_COMMAND\x10\t\x12\x1f\n\x1bMESSAGEFAULT_ERROR_DECODING\x10\n\x12\x1f\n\x1bMESSAGEFAULT_ERROR_INTERNAL\x10\x0b\x12,\n(MESSAGEFAULT_ERROR_WRONG_PERSONALIZATION\x10\x0c\x12$\n MESSAGEFAULT_ERROR_BAD_PARAMETER\x10\r\x12\'\n#MESSAGEFAULT_ERROR_KEYCHAIN_IS_FULL\x10\x0e\x12&\n\"MESSAGEFAULT_ERROR_INCORRECT_EPOCH\x10\x0f\x12*\n&MESSAGEFAULT_ERROR_IV_INCORRECT_LENGTH\x10\x10\x12#\n\x1fMESSAGEFAULT_ERROR_TIME_EXPIRED\x10\x11\x12\x34\n0MESSAGEFAULT_ERROR_NOT_PROVISIONED_WITH_IDENTITY\x10\x12\x12.\n*MESSAGEFAULT_ERROR_COULD_NOT_HASH_METADATA\x10\x13\x12,\n(MESSAGEFAULT_ERROR_TIME_TO_LIVE_TOO_LONG\x10\x14\x12-\n)MESSAGEFAULT_ERROR_REMOTE_ACCESS_DISABLED\x10\x15\x12\x35\n1MESSAGEFAULT_ERROR_REMOTE_SERVICE_ACCESS_DISABLED\x10\x16\x12;\n7MESSAGEFAULT_ERROR_COMMAND_REQUIRES_ACCOUNT_CREDENTIALS\x10\x17\x12+\n\'MESSAGEFAULT_ERROR_REQUEST_MTU_EXCEEDED\x10\x18\x12,\n(MESSAGEFAULT_ERROR_RESPONSE_MTU_EXCEEDED\x10\x19\x12\'\n#MESSAGEFAULT_ERROR_REPEATED_COUNTER\x10\x1a\x12)\n%MESSAGEFAULT_ERROR_INVALID_KEY_HANDLE\x10\x1b\x12\x33\n/MESSAGEFAULT_ERROR_REQUIRES_RESPONSE_ENCRYPTION\x10\x1c*9\n\x05\x46lags\x12\x15\n\x11\x46LAG_USER_COMMAND\x10\x00\x12\x19\n\x15\x46LAG_ENCRYPT_RESPONSE\x10\x01\x42u\n$com.tesla.generated.universalmessageZMgithub.com/teslamotors/vehicle-command/pkg/protocol/protobuf/universalmessageb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1dtesla/universal_message.proto\x12\x10UniversalMessage\x1a\x16tesla/signatures.proto\"g\n\x0b\x44\x65stination\x12*\n\x06\x64omain\x18\x01 \x01(\x0e\x32\x18.UniversalMessage.DomainH\x00\x12\x19\n\x0frouting_address\x18\x02 \x01(\x0cH\x00\x42\x11\n\x0fsub_destination\"\x8e\x01\n\rMessageStatus\x12=\n\x10operation_status\x18\x01 \x01(\x0e\x32#.UniversalMessage.OperationStatus_E\x12>\n\x14signed_message_fault\x18\x02 \x01(\x0e\x32 .UniversalMessage.MessageFault_E\";\n\x12SessionInfoRequest\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\x12\x11\n\tchallenge\x18\x02 \x01(\x0c\"\xd6\x03\n\x0fRoutableMessage\x12\x35\n\x0eto_destination\x18\x06 \x01(\x0b\x32\x1d.UniversalMessage.Destination\x12\x37\n\x10\x66rom_destination\x18\x07 \x01(\x0b\x32\x1d.UniversalMessage.Destination\x12#\n\x19protobuf_message_as_bytes\x18\n \x01(\x0cH\x00\x12\x44\n\x14session_info_request\x18\x0e \x01(\x0b\x32$.UniversalMessage.SessionInfoRequestH\x00\x12\x16\n\x0csession_info\x18\x0f \x01(\x0cH\x00\x12\x33\n\x0esignature_data\x18\r \x01(\x0b\x32\x19.Signatures.SignatureDataH\x01\x12<\n\x13signedMessageStatus\x18\x0c \x01(\x0b\x32\x1f.UniversalMessage.MessageStatus\x12\x14\n\x0crequest_uuid\x18\x32 \x01(\x0c\x12\x0c\n\x04uuid\x18\x33 \x01(\x0c\x12\r\n\x05\x66lags\x18\x34 \x01(\rB\t\n\x07payloadB\r\n\x0bsub_sigDataJ\x04\x08\x01\x10\x06J\x04\x08\x10\x10)J\x04\x08\x0b\x10\x0c*\x9f\x01\n\x06\x44omain\x12\x14\n\x10\x44OMAIN_BROADCAST\x10\x00\x12\x1b\n\x17\x44OMAIN_VEHICLE_SECURITY\x10\x02\x12\x17\n\x13\x44OMAIN_INFOTAINMENT\x10\x03\x12\x10\n\x0c\x44OMAIN_AUTHD\x10\x05\x12\x18\n\x14\x44OMAIN_ENERGY_DEVICE\x10\x07\x12\x1d\n\x19\x44OMAIN_ENERGY_DEVICE_AUTH\x10\x08*`\n\x11OperationStatus_E\x12\x16\n\x12OPERATIONSTATUS_OK\x10\x00\x12\x18\n\x14OPERATIONSTATUS_WAIT\x10\x01\x12\x19\n\x15OPERATIONSTATUS_ERROR\x10\x02*\xe4\t\n\x0eMessageFault_E\x12\x1b\n\x17MESSAGEFAULT_ERROR_NONE\x10\x00\x12\x1b\n\x17MESSAGEFAULT_ERROR_BUSY\x10\x01\x12\x1e\n\x1aMESSAGEFAULT_ERROR_TIMEOUT\x10\x02\x12%\n!MESSAGEFAULT_ERROR_UNKNOWN_KEY_ID\x10\x03\x12#\n\x1fMESSAGEFAULT_ERROR_INACTIVE_KEY\x10\x04\x12(\n$MESSAGEFAULT_ERROR_INVALID_SIGNATURE\x10\x05\x12/\n+MESSAGEFAULT_ERROR_INVALID_TOKEN_OR_COUNTER\x10\x06\x12.\n*MESSAGEFAULT_ERROR_INSUFFICIENT_PRIVILEGES\x10\x07\x12&\n\"MESSAGEFAULT_ERROR_INVALID_DOMAINS\x10\x08\x12&\n\"MESSAGEFAULT_ERROR_INVALID_COMMAND\x10\t\x12\x1f\n\x1bMESSAGEFAULT_ERROR_DECODING\x10\n\x12\x1f\n\x1bMESSAGEFAULT_ERROR_INTERNAL\x10\x0b\x12,\n(MESSAGEFAULT_ERROR_WRONG_PERSONALIZATION\x10\x0c\x12$\n MESSAGEFAULT_ERROR_BAD_PARAMETER\x10\r\x12\'\n#MESSAGEFAULT_ERROR_KEYCHAIN_IS_FULL\x10\x0e\x12&\n\"MESSAGEFAULT_ERROR_INCORRECT_EPOCH\x10\x0f\x12*\n&MESSAGEFAULT_ERROR_IV_INCORRECT_LENGTH\x10\x10\x12#\n\x1fMESSAGEFAULT_ERROR_TIME_EXPIRED\x10\x11\x12\x34\n0MESSAGEFAULT_ERROR_NOT_PROVISIONED_WITH_IDENTITY\x10\x12\x12.\n*MESSAGEFAULT_ERROR_COULD_NOT_HASH_METADATA\x10\x13\x12,\n(MESSAGEFAULT_ERROR_TIME_TO_LIVE_TOO_LONG\x10\x14\x12-\n)MESSAGEFAULT_ERROR_REMOTE_ACCESS_DISABLED\x10\x15\x12\x35\n1MESSAGEFAULT_ERROR_REMOTE_SERVICE_ACCESS_DISABLED\x10\x16\x12;\n7MESSAGEFAULT_ERROR_COMMAND_REQUIRES_ACCOUNT_CREDENTIALS\x10\x17\x12+\n\'MESSAGEFAULT_ERROR_REQUEST_MTU_EXCEEDED\x10\x18\x12,\n(MESSAGEFAULT_ERROR_RESPONSE_MTU_EXCEEDED\x10\x19\x12\'\n#MESSAGEFAULT_ERROR_REPEATED_COUNTER\x10\x1a\x12)\n%MESSAGEFAULT_ERROR_INVALID_KEY_HANDLE\x10\x1b\x12\x33\n/MESSAGEFAULT_ERROR_REQUIRES_RESPONSE_ENCRYPTION\x10\x1c*9\n\x05\x46lags\x12\x15\n\x11\x46LAG_USER_COMMAND\x10\x00\x12\x19\n\x15\x46LAG_ENCRYPT_RESPONSE\x10\x01\x42u\n$com.tesla.generated.universalmessageZMgithub.com/teslamotors/vehicle-command/pkg/protocol/protobuf/universalmessageb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -33,14 +33,14 @@ if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'\n$com.tesla.generated.universalmessageZMgithub.com/teslamotors/vehicle-command/pkg/protocol/protobuf/universalmessage' - _globals['_DOMAIN']._serialized_start=859 - _globals['_DOMAIN']._serialized_end=943 - _globals['_OPERATIONSTATUS_E']._serialized_start=945 - _globals['_OPERATIONSTATUS_E']._serialized_end=1041 - _globals['_MESSAGEFAULT_E']._serialized_start=1044 - _globals['_MESSAGEFAULT_E']._serialized_end=2296 - _globals['_FLAGS']._serialized_start=2298 - _globals['_FLAGS']._serialized_end=2355 + _globals['_DOMAIN']._serialized_start=860 + _globals['_DOMAIN']._serialized_end=1019 + _globals['_OPERATIONSTATUS_E']._serialized_start=1021 + _globals['_OPERATIONSTATUS_E']._serialized_end=1117 + _globals['_MESSAGEFAULT_E']._serialized_start=1120 + _globals['_MESSAGEFAULT_E']._serialized_end=2372 + _globals['_FLAGS']._serialized_start=2374 + _globals['_FLAGS']._serialized_end=2431 _globals['_DESTINATION']._serialized_start=75 _globals['_DESTINATION']._serialized_end=178 _globals['_MESSAGESTATUS']._serialized_start=181 diff --git a/tesla_fleet_api/tesla/vehicle/proto/universal_message_pb2.pyi b/tesla_fleet_api/tesla/vehicle/proto/universal_message_pb2.pyi index 6083cbc..66ae5e6 100644 --- a/tesla_fleet_api/tesla/vehicle/proto/universal_message_pb2.pyi +++ b/tesla_fleet_api/tesla/vehicle/proto/universal_message_pb2.pyi @@ -12,6 +12,9 @@ class Domain(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): DOMAIN_BROADCAST: _ClassVar[Domain] DOMAIN_VEHICLE_SECURITY: _ClassVar[Domain] DOMAIN_INFOTAINMENT: _ClassVar[Domain] + DOMAIN_AUTHD: _ClassVar[Domain] + DOMAIN_ENERGY_DEVICE: _ClassVar[Domain] + DOMAIN_ENERGY_DEVICE_AUTH: _ClassVar[Domain] class OperationStatus_E(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () @@ -58,6 +61,9 @@ class Flags(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): DOMAIN_BROADCAST: Domain DOMAIN_VEHICLE_SECURITY: Domain DOMAIN_INFOTAINMENT: Domain +DOMAIN_AUTHD: Domain +DOMAIN_ENERGY_DEVICE: Domain +DOMAIN_ENERGY_DEVICE_AUTH: Domain OPERATIONSTATUS_OK: OperationStatus_E OPERATIONSTATUS_WAIT: OperationStatus_E OPERATIONSTATUS_ERROR: OperationStatus_E diff --git a/tools/regenerate_protos.sh b/tools/regenerate_protos.sh index d7226ab..f4b3eb6 100755 --- a/tools/regenerate_protos.sh +++ b/tools/regenerate_protos.sh @@ -40,6 +40,7 @@ PROTOS=( vehicle.proto car_server.proto universal_message.proto + session.proto ) # Copy the top-level .proto files into proto/tesla/, rewriting imports of our @@ -50,7 +51,7 @@ import re, sys src, dst = sys.argv[1], sys.argv[2] own = {"common.proto","errors.proto","keys.proto","signatures.proto", "managed_charging.proto","vcsec.proto","vehicle.proto", - "car_server.proto","universal_message.proto"} + "car_server.proto","universal_message.proto","session.proto"} text = open(src).read() def repl(m): name = m.group(1)