tesla-fleet-api 1.0.3__py3-none-any.whl → 1.0.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tesla_fleet_api/const.py CHANGED
@@ -3,7 +3,7 @@
3
3
  from enum import Enum
4
4
  import logging
5
5
 
6
- VERSION = "1.0.3"
6
+ VERSION = "1.0.5"
7
7
  LOGGER = logging.getLogger(__package__)
8
8
  SERVERS = {
9
9
  "na": "https://fleet-api.prd.na.vn.cloud.tesla.com",
@@ -21,7 +21,6 @@ class ResponseError(TeslaFleetError):
21
21
  """The response from the server was not JSON."""
22
22
 
23
23
  message = "The response from the server was not JSON."
24
- data: str | None = None
25
24
 
26
25
 
27
26
  class InvalidCommand(TeslaFleetError):
@@ -2,6 +2,7 @@
2
2
 
3
3
  import hashlib
4
4
  import re
5
+ from google.protobuf.json_format import MessageToJson, MessageToDict
5
6
  from bleak.backends.device import BLEDevice
6
7
  from cryptography.hazmat.primitives.asymmetric import ec
7
8
 
@@ -43,3 +44,11 @@ class Vehicles(dict[str, VehicleBluetooth]):
43
44
  vehicle = VehicleBluetooth(self._parent, vin, key, device)
44
45
  self[vin] = vehicle
45
46
  return vehicle
47
+
48
+ def toJson(message) -> str:
49
+ """Convert a protobuf message to JSON."""
50
+ return MessageToJson(message, preserving_proto_field_name=True)
51
+
52
+ def toDict(message) -> dict:
53
+ """Convert a protobuf message to a dictionary."""
54
+ return MessageToDict(message, preserving_proto_field_name=True)
@@ -159,11 +159,12 @@ class EnergySites(dict[int, EnergySite]):
159
159
  """Class describing the Tesla Fleet API partner endpoints"""
160
160
 
161
161
  _parent: TeslaFleetApi
162
+ Site = EnergySite
162
163
 
163
164
  def __init__(self, parent: TeslaFleetApi):
164
165
  self._parent = parent
165
166
 
166
167
  def create(self, energy_site_id: int) -> EnergySite:
167
168
  """Create a specific energy site."""
168
- self[energy_site_id] = EnergySite(self._parent, energy_site_id)
169
+ self[energy_site_id] = self.Site(self._parent, energy_site_id)
169
170
  return self[energy_site_id]
@@ -4,7 +4,7 @@ import hashlib
4
4
  import asyncio
5
5
  from typing import TYPE_CHECKING
6
6
  from google.protobuf.message import DecodeError
7
-
7
+ from bleak_retry_connector import establish_connection, MAX_CONNECT_ATTEMPTS
8
8
  from bleak import BleakClient, BleakScanner
9
9
  from bleak.backends.characteristic import BleakGATTCharacteristic
10
10
  from bleak.backends.device import BLEDevice
@@ -19,17 +19,14 @@ from tesla_fleet_api.const import (
19
19
  BluetoothVehicleData
20
20
  )
21
21
  from tesla_fleet_api.exceptions import (
22
- MESSAGE_FAULTS,
23
22
  WHITELIST_OPERATION_STATUS,
24
23
  WhitelistOperationStatus,
25
- NotOnWhitelistFault,
26
24
  )
27
25
 
28
26
  # Protocol
29
27
  from tesla_fleet_api.tesla.vehicle.proto.car_server_pb2 import (
30
28
  Action,
31
29
  VehicleAction,
32
- Response,
33
30
  GetVehicleData,
34
31
  GetChargeState,
35
32
  GetClimateState,
@@ -44,10 +41,6 @@ from tesla_fleet_api.tesla.vehicle.proto.car_server_pb2 import (
44
41
  GetSoftwareUpdateState,
45
42
  GetParentalControlsState,
46
43
  )
47
- from tesla_fleet_api.tesla.vehicle.proto.signatures_pb2 import (
48
- SessionInfo,
49
- Session_Info_Status
50
- )
51
44
  from tesla_fleet_api.tesla.vehicle.proto.universal_message_pb2 import (
52
45
  Destination,
53
46
  Domain,
@@ -84,6 +77,7 @@ class VehicleBluetooth(Commands):
84
77
  """Class describing the Tesla Fleet API vehicle endpoints and commands for a specific vehicle with command signing."""
85
78
 
86
79
  ble_name: str
80
+ device: BLEDevice
87
81
  client: BleakClient
88
82
  _queues: dict[Domain, asyncio.Queue]
89
83
  _ekey: ec.EllipticCurvePublicKey
@@ -103,26 +97,38 @@ class VehicleBluetooth(Commands):
103
97
  if device is not None:
104
98
  self.client = BleakClient(device, services=[SERVICE_UUID])
105
99
 
106
- async def find_client(self, scanner: BleakScanner = BleakScanner()) -> BleakClient:
100
+ async def find_vehicle(self, name: str | None = None, address: str | None = None, scanner: BleakScanner = BleakScanner()) -> BLEDevice:
107
101
  """Find the Tesla BLE device."""
108
-
109
- device = await scanner.find_device_by_name(self.ble_name)
102
+ if name is not None:
103
+ device = await scanner.find_device_by_name(name)
104
+ elif address is not None:
105
+ device = await scanner.find_device_by_address(address)
106
+ else:
107
+ device = await scanner.find_device_by_name(self.ble_name)
110
108
  if not device:
111
109
  raise ValueError(f"Device {self.ble_name} not found")
112
- self.client = BleakClient(device, services=[SERVICE_UUID])
113
- LOGGER.debug(f"Discovered device {device.name} {device.address}")
114
- return self.client
110
+ self.device = device
111
+ return self.device
112
+
113
+ def set_device(self, device: BLEDevice) -> None:
114
+ self.device = device
115
115
 
116
- def create_client(self, device: str|BLEDevice) -> BleakClient:
117
- """Create a client using a MAC address or Bleak Device."""
118
- self.client = BleakClient(device, services=[SERVICE_UUID])
119
- return self.client
116
+ def get_device(self) -> BLEDevice:
117
+ return self.device
120
118
 
121
- async def connect(self, device: str|BLEDevice | None = None) -> None:
119
+ async def connect(self, device: BLEDevice | None = None, max_attempts: int = MAX_CONNECT_ATTEMPTS) -> None:
122
120
  """Connect to the Tesla BLE device."""
123
- if device is not None:
124
- self.create_client(device)
125
- await self.client.connect()
121
+ if device:
122
+ self.device = device
123
+ if not self.device:
124
+ raise ValueError(f"Device {self.ble_name} not found")
125
+ self.client = await establish_connection(
126
+ BleakClient,
127
+ self.device,
128
+ self.vin,
129
+ max_attempts=max_attempts,
130
+ ble_device_callback=self.get_device
131
+ )
126
132
  await self.client.start_notify(READ_UUID, self._on_notify)
127
133
 
128
134
  async def disconnect(self) -> bool:
@@ -144,34 +150,43 @@ class VehicleBluetooth(Commands):
144
150
  self._recv += data
145
151
  else:
146
152
  self._recv_len = int.from_bytes(data[:2], 'big')
153
+ if self._recv_len > 1024:
154
+ LOGGER.error("Parsed very large message length")
155
+ self._recv = bytearray()
156
+ self._recv_len = 0
157
+ return
147
158
  self._recv = data[2:]
148
- LOGGER.debug(f"Received {len(self._recv)} of {self._recv_len} bytes")
149
- while len(self._recv) > self._recv_len:
150
- LOGGER.warn(f"Received more data than expected: {len(self._recv)} > {self._recv_len}")
151
- await self._on_message(bytes(self._recv[:self._recv_len]))
152
- self._recv_len = int.from_bytes(self._recv[self._recv_len:self._recv_len+2], 'big')
153
- self._recv = self._recv[self._recv_len+2:]
154
- continue
155
- if len(self._recv) == self._recv_len:
156
- await self._on_message(bytes(self._recv))
157
- self._recv = bytearray()
158
- self._recv_len = 0
159
-
160
- async def _on_message(self, data:bytes) -> None:
159
+ #while len(self._recv) > self._recv_len:
160
+ #
161
+ # # Maybe this needs to trigger a reset
162
+ # await self._on_message(bytes(self._recv[:self._recv_len]))
163
+ # self._recv_len = int.from_bytes(self._recv[self._recv_len:self._recv_len+2], 'big')
164
+ # self._recv = self._recv[self._recv_len+2:]
165
+ # continue
166
+ if len(self._recv) >= self._recv_len:
167
+ if len(self._recv) > self._recv_len:
168
+ LOGGER.debug(f"Received more data than expected: {len(self._recv)} > {self._recv_len}")
169
+ try:
170
+ msg = RoutableMessage.FromString(bytes(self._recv[:self._recv_len]))
171
+ await self._on_message(msg)
172
+ self._recv = bytearray()
173
+ self._recv_len = 0
174
+ except DecodeError:
175
+ # Attempt parsing the whole payload
176
+ msg = RoutableMessage.FromString(bytes(self._recv))
177
+ LOGGER.warn(f"Parsed more data than length: {len(self._recv)} > {self._recv_len}")
178
+ await self._on_message(msg)
179
+ self._recv = bytearray()
180
+ self._recv_len = 0
181
+
182
+ async def _on_message(self, msg: RoutableMessage) -> None:
161
183
  """Receive messages from the Tesla BLE data."""
162
- try:
163
- msg = RoutableMessage.FromString(data)
164
- except DecodeError as e:
165
- LOGGER.error(f"Error parsing message: {e}")
166
- self._recv = bytearray()
167
- self._recv_len = 0
168
- return
169
184
 
170
185
  if(msg.to_destination.routing_address != self._from_destination):
171
186
  # Ignore ephemeral key broadcasts
172
187
  return
173
188
 
174
- LOGGER.info(f"Received response: {msg}")
189
+ LOGGER.debug(f"Received response: {msg}")
175
190
  await self._queues[msg.from_destination.domain].put(msg)
176
191
 
177
192
  async def _send(self, msg: RoutableMessage, requires: str) -> RoutableMessage:
@@ -188,7 +203,7 @@ class VehicleBluetooth(Commands):
188
203
  await self.client.write_gatt_char(WRITE_UUID, payload, True)
189
204
 
190
205
  # Process the response
191
- async with asyncio.timeout(10):
206
+ async with asyncio.timeout(2):
192
207
  while True:
193
208
  resp = await self._queues[domain].get()
194
209
  LOGGER.debug(f"Received message {resp}")
@@ -221,7 +236,7 @@ class VehicleBluetooth(Commands):
221
236
  ),
222
237
  protobuf_message_as_bytes=request.SerializeToString(),
223
238
  )
224
- resp = await self._send(msg)
239
+ resp = await self._send(msg, "protobuf_message_as_bytes")
225
240
  respMsg = FromVCSECMessage.FromString(resp.protobuf_message_as_bytes)
226
241
  if(respMsg.commandStatus.whitelistOperationStatus.whitelistOperationInformation):
227
242
  if(respMsg.commandStatus.whitelistOperationStatus.whitelistOperationInformation < len(WHITELIST_OPERATION_STATUS)):
@@ -5,14 +5,6 @@ from typing import TYPE_CHECKING
5
5
 
6
6
  from tesla_fleet_api.tesla.vehicle.fleet import VehicleFleet
7
7
  from tesla_fleet_api.tesla.vehicle.commands import Commands
8
- from tesla_fleet_api.exceptions import (
9
- MESSAGE_FAULTS,
10
- NotOnWhitelistFault,
11
- )
12
- from tesla_fleet_api.tesla.vehicle.proto.signatures_pb2 import (
13
- Session_Info_Status,
14
- SessionInfo,
15
- )
16
8
  from tesla_fleet_api.tesla.vehicle.proto.universal_message_pb2 import (
17
9
  RoutableMessage,
18
10
  )
@@ -41,9 +33,6 @@ class VehicleSigned(VehicleFleet, Commands):
41
33
  json = await self.signed_command(
42
34
  base64.b64encode(msg.SerializeToString()).decode()
43
35
  )
44
-
45
36
  resp = RoutableMessage.FromString(base64.b64decode(json["response"]))
46
-
47
37
  self.validate_msg(resp)
48
-
49
38
  return resp
@@ -1,10 +1,18 @@
1
1
  from __future__ import annotations
2
2
  from typing import TYPE_CHECKING
3
3
 
4
-
5
4
  if TYPE_CHECKING:
6
5
  from tesla_fleet_api.tesla.tesla import Tesla
7
6
 
7
+ MODELS = {
8
+ "S": "Model S",
9
+ "X": "Model X",
10
+ "3": "Model 3",
11
+ "Y": "Model Y",
12
+ "C": "Cybertruck",
13
+ "R": "Roadster",
14
+ "T": "Semi",
15
+ }
8
16
 
9
17
  class Vehicle:
10
18
  """Base class describing a Tesla vehicle."""
@@ -14,6 +22,12 @@ class Vehicle:
14
22
  def __init__(self, parent: Tesla, vin: str):
15
23
  self.vin = vin
16
24
 
17
- def pre2021(self, vin: str) -> bool:
25
+
26
+ def pre2021(self) -> bool:
18
27
  """Checks if a vehicle is a pre-2021 model S or X."""
19
- return vin[3] in ["S", "X"] and (vin[9] <= "L" or (vin[9] == "M" and vin[7] in ['1', '2', '3', '4']))
28
+ return self.vin[3] in ["S", "X"] and (self.vin[9] <= "L" or (self.vin[9] == "M" and self.vin[7] in ['1', '2', '3', '4']))
29
+
30
+ @property
31
+ def model(self) -> str:
32
+ """Returns the model of the vehicle."""
33
+ return MODELS.get(self.vin[3], "Unknown")
@@ -15,25 +15,28 @@ class Vehicles(dict[str, Vehicle]):
15
15
  """Class containing and creating vehicles."""
16
16
 
17
17
  _parent: TeslaFleetApi
18
+ Fleet = VehicleFleet
19
+ Signed = VehicleSigned
20
+ Bluetooth = VehicleBluetooth
18
21
 
19
22
  def __init__(self, parent: TeslaFleetApi):
20
23
  self._parent = parent
21
24
 
22
25
  def createFleet(self, vin: str) -> VehicleFleet:
23
26
  """Creates a Fleet API vehicle."""
24
- vehicle = VehicleFleet(self._parent, vin)
27
+ vehicle = self.Fleet(self._parent, vin)
25
28
  self[vin] = vehicle
26
29
  return vehicle
27
30
 
28
31
  def createSigned(self, vin: str) -> VehicleSigned:
29
32
  """Creates a Fleet API vehicle that uses command protocol."""
30
- vehicle = VehicleSigned(self._parent, vin)
33
+ vehicle = self.Signed(self._parent, vin)
31
34
  self[vin] = vehicle
32
35
  return vehicle
33
36
 
34
37
  def createBluetooth(self, vin: str):
35
38
  """Creates a bluetooth vehicle that uses command protocol."""
36
- vehicle = VehicleBluetooth(self._parent, vin)
39
+ vehicle = self.Bluetooth(self._parent, vin)
37
40
  self[vin] = vehicle
38
41
  return vehicle
39
42
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tesla_fleet_api
3
- Version: 1.0.3
3
+ Version: 1.0.5
4
4
  Summary: Tesla Fleet API library for Python
5
5
  Home-page: https://github.com/Teslemetry/python-tesla-fleet-api
6
6
  Author: Brett Adams
@@ -18,6 +18,7 @@ Requires-Dist: aiolimiter
18
18
  Requires-Dist: cryptography
19
19
  Requires-Dist: protobuf
20
20
  Requires-Dist: bleak
21
+ Requires-Dist: bleak-retry-connector
21
22
  Dynamic: author
22
23
  Dynamic: author-email
23
24
  Dynamic: classifier
@@ -29,10 +30,11 @@ Dynamic: requires-python
29
30
  Dynamic: summary
30
31
 
31
32
  # Tesla Fleet Api
32
- Python library for Tesla Fleet API and Teslemetry.
33
+ Python library for Tesla Fleet API and Tesla Command Protocol, including signed commands and encrypted local Bluetooth (BLE). Also provides interfaces for Teslemetry and Tessie.
33
34
 
34
- Based on [Tesla Developer documentation](https://developer.tesla.com/docs/fleet-api).
35
+ Based on [Tesla Developer documentation](https://developer.tesla.com/docs/fleet-api) and [Tesla Command Protocol](https://github.com/teslamotors/vehicle-command/blob/main/pkg/protocol/protocol.md)
35
36
 
37
+ **Documentation is currently outdated for V1.0.X**
36
38
 
37
39
  ## TeslaFleetApi
38
40
  This is the base class, however can also be used directly if you have a valid user access_token.
@@ -1,23 +1,23 @@
1
1
  tesla_fleet_api/__init__.py,sha256=3DZMoZ-5srW-7SooAjqcRubQDuZPY8rMKH7eqIp4qtg,392
2
- tesla_fleet_api/const.py,sha256=RB2ZgI0_y0DkLUJtMSpklCRH9aNlrninO4zot5cA-H0,3748
3
- tesla_fleet_api/exceptions.py,sha256=1EkMnDxQYxZBn4ApjGodIBZyNPu9oa_Pgpdl6lQQ5gk,35523
2
+ tesla_fleet_api/const.py,sha256=1GEFPEYxka-ADBs_MWNaxdr2FPH7ObxaKRX2NvFrGaY,3748
3
+ tesla_fleet_api/exceptions.py,sha256=iZJv_6YJO6wK_lYvR18SugpNk-ZTzElmcsOqNqd05M0,35495
4
4
  tesla_fleet_api/ratecalculator.py,sha256=4lz8yruUeouHXh_3ezsXX-CTpIegp1T1J4VuRV_qdHA,1791
5
5
  tesla_fleet_api/tesla/__init__.py,sha256=Cvpqu8OaOFmbuwu9KjgYrje8eVluDp2IU_zwdtXbmO0,282
6
- tesla_fleet_api/tesla/bluetooth.py,sha256=UCKXF3u_TfuGlJ-LYEIxgmbbzlElZfuJOInsWJ_ltr0,1471
6
+ tesla_fleet_api/tesla/bluetooth.py,sha256=WhDo66OEwCGhvsGskvWcGUiNv6eyFsdEiQdIm571jQM,1835
7
7
  tesla_fleet_api/tesla/charging.py,sha256=D7I7cAf-3-95sIjyP6wpVqCq9Cppj6U-VPFQGpQQ8bs,1704
8
- tesla_fleet_api/tesla/energysite.py,sha256=96Q5npsJ2YIa257o_NL5_3gJNUS-ioAL7sTeQeGPgAM,6110
8
+ tesla_fleet_api/tesla/energysite.py,sha256=vStffklBQfQNAO_1wrHLFu7BlBCTVVbLh7_IrAUL3wg,6131
9
9
  tesla_fleet_api/tesla/fleet.py,sha256=X74tzwGO9w65j9YUGuW04CwG7Bx6biEHwxIjWGCzB4c,5670
10
10
  tesla_fleet_api/tesla/oauth.py,sha256=aWBsWmnM-QxzaU8W9TXVNxGsYn_LraXnpexwdE8wOqo,4104
11
11
  tesla_fleet_api/tesla/partner.py,sha256=e-l6sEP6-IupjFEQieSUjhhvRXF3aL4ebPNahcGFRCE,1238
12
12
  tesla_fleet_api/tesla/tesla.py,sha256=Jlz90-fM0nJbhnQN0k3ukNv59-9KqZZbyQ91IiLIbfo,2010
13
13
  tesla_fleet_api/tesla/user.py,sha256=w8rwiAOIFjuDus8M0RpZ0wucJtw8kYFKtJfYVk7Ekr0,1194
14
14
  tesla_fleet_api/tesla/vehicle/__init__.py,sha256=3A5_wTQHofRShof4pUNOtF78-7lUh62uz2jq2ecnmRY,381
15
- tesla_fleet_api/tesla/vehicle/bluetooth.py,sha256=fhV24gni_tkhRJmK5I-xUX0dqxEw_lumRViEwmkXDSw,15665
15
+ tesla_fleet_api/tesla/vehicle/bluetooth.py,sha256=25UlaAatKxu_NzbG5te5sKHeM9jAufj42LjGJaIKNHg,16476
16
16
  tesla_fleet_api/tesla/vehicle/commands.py,sha256=zgfc2yo1UxEh8ePqSb-4h0UTK0RmpCG_9LZX44CES2c,51268
17
17
  tesla_fleet_api/tesla/vehicle/fleet.py,sha256=K9BVZj6CChJSDSMFroa7Cz0KrsYWj32ILtQumarkLaU,32080
18
- tesla_fleet_api/tesla/vehicle/signed.py,sha256=ggdtq8PydKoCk044L5b2262nfNzZqPBnsJ4SonTFbb4,1539
19
- tesla_fleet_api/tesla/vehicle/vehicle.py,sha256=TyW5-LRlgRulWsm2indE3utSTdrJJRfG7H45Cc-ZASk,505
20
- tesla_fleet_api/tesla/vehicle/vehicles.py,sha256=wyMLfHNK_QaNTDtU9mkGRl2fB3Gb6lvmSfcgXzKu7WY,1565
18
+ tesla_fleet_api/tesla/vehicle/signed.py,sha256=RUzVnZIfykz3YZW2gaxd1iaN1i8LkLaEoiXrbqZn9kg,1339
19
+ tesla_fleet_api/tesla/vehicle/vehicle.py,sha256=nIhfn2hGuHJoAj-LiipcZdS4abdrWI6yST2cuE-w23s,810
20
+ tesla_fleet_api/tesla/vehicle/vehicles.py,sha256=axiH33YRnkYcbQmolbL85PF69SRI_5FA5T10v-cMmBc,1644
21
21
  tesla_fleet_api/tesla/vehicle/proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  tesla_fleet_api/tesla/vehicle/proto/__init__.pyi,sha256=qFXWNIgl71wB260u-XPzaAwWAHL6krw21q-aXnBtop0,252
23
23
  tesla_fleet_api/tesla/vehicle/proto/car_server_pb2.py,sha256=v_eb4NDIkx_ZYPYW29_EFRky5vQ4b2q14gwuQSbouYw,29202
@@ -44,8 +44,8 @@ tesla_fleet_api/teslemetry/vehicle.py,sha256=9_2N1iNNDouqfb6YBBWAFjnlzVRTf5frhXi
44
44
  tesla_fleet_api/tessie/__init__.py,sha256=9lhQJaB6X4PObUL9QdaaZYqs2BxiTidu3zmHcBESLVw,78
45
45
  tesla_fleet_api/tessie/tessie.py,sha256=qdMZ61TcQi5JRuv2qaxuLHtOuy8WZJ1WNqWg5WDAwwU,2615
46
46
  tesla_fleet_api/tessie/vehicle.py,sha256=9khv4oCkGGLxHzQ2FYhDPH7wczxEDiUppsDXalawarE,1125
47
- tesla_fleet_api-1.0.3.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
48
- tesla_fleet_api-1.0.3.dist-info/METADATA,sha256=ARBm4GR744Ru7F2JumVFrw4UgeSxnYeV1k25KlzgqhY,4056
49
- tesla_fleet_api-1.0.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
50
- tesla_fleet_api-1.0.3.dist-info/top_level.txt,sha256=jeNbog_1saXBFrGpom9WyPWmilxsyP3szL_G7JLWQfM,16
51
- tesla_fleet_api-1.0.3.dist-info/RECORD,,
47
+ tesla_fleet_api-1.0.5.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
48
+ tesla_fleet_api-1.0.5.dist-info/METADATA,sha256=nL7RUQYO8-i8R0-zkSQBNvJD7BtruTEkNxywMB5lsT4,4382
49
+ tesla_fleet_api-1.0.5.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
50
+ tesla_fleet_api-1.0.5.dist-info/top_level.txt,sha256=jeNbog_1saXBFrGpom9WyPWmilxsyP3szL_G7JLWQfM,16
51
+ tesla_fleet_api-1.0.5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (75.8.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5