tesla-fleet-api 0.9.5__py3-none-any.whl → 0.9.6__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.
Files changed (32) hide show
  1. tesla_fleet_api/const.py +1 -1
  2. tesla_fleet_api/exceptions.py +239 -46
  3. tesla_fleet_api/pb2/__init__.pyi +9 -0
  4. tesla_fleet_api/pb2/car_server_pb2.py +164 -386
  5. tesla_fleet_api/pb2/car_server_pb2.pyi +904 -0
  6. tesla_fleet_api/pb2/common_pb2.py +26 -60
  7. tesla_fleet_api/pb2/common_pb2.pyi +130 -0
  8. tesla_fleet_api/pb2/errors_pb2.py +10 -22
  9. tesla_fleet_api/pb2/errors_pb2.pyi +32 -0
  10. tesla_fleet_api/pb2/keys_pb2.py +8 -16
  11. tesla_fleet_api/pb2/keys_pb2.pyi +21 -0
  12. tesla_fleet_api/pb2/managed_charging_pb2.py +8 -14
  13. tesla_fleet_api/pb2/managed_charging_pb2.pyi +17 -0
  14. tesla_fleet_api/pb2/signatures_pb2.py +28 -75
  15. tesla_fleet_api/pb2/signatures_pb2.pyi +152 -0
  16. tesla_fleet_api/pb2/universal_message_pb2.py +22 -78
  17. tesla_fleet_api/pb2/universal_message_pb2.pyi +148 -0
  18. tesla_fleet_api/pb2/vcsec_pb2.py +70 -236
  19. tesla_fleet_api/pb2/vcsec_pb2.pyi +482 -0
  20. tesla_fleet_api/pb2/vehicle_pb2.py +114 -267
  21. tesla_fleet_api/pb2/vehicle_pb2.pyi +1183 -0
  22. tesla_fleet_api/teslafleetapi.py +7 -5
  23. tesla_fleet_api/teslafleetoauth.py +1 -1
  24. tesla_fleet_api/teslemetry.py +2 -2
  25. tesla_fleet_api/vehicle.py +1 -1
  26. tesla_fleet_api/vehiclesigned.py +142 -94
  27. {tesla_fleet_api-0.9.5.dist-info → tesla_fleet_api-0.9.6.dist-info}/METADATA +12 -3
  28. tesla_fleet_api-0.9.6.dist-info/RECORD +42 -0
  29. {tesla_fleet_api-0.9.5.dist-info → tesla_fleet_api-0.9.6.dist-info}/WHEEL +1 -1
  30. tesla_fleet_api-0.9.5.dist-info/RECORD +0 -33
  31. {tesla_fleet_api-0.9.5.dist-info → tesla_fleet_api-0.9.6.dist-info}/LICENSE +0 -0
  32. {tesla_fleet_api-0.9.5.dist-info → tesla_fleet_api-0.9.6.dist-info}/top_level.txt +0 -0
@@ -106,6 +106,11 @@ class TeslaFleetApi:
106
106
  if access_token := await self.refresh_hook():
107
107
  self.access_token = access_token
108
108
 
109
+ headers = {
110
+ "Authorization": f"Bearer {self.access_token}",
111
+ "X-Library": f"python tesla_fleet_api {VERSION}",
112
+ }
113
+
109
114
  # Remove None values from params and json
110
115
  if params:
111
116
  params = {k: v for k, v in params.items() if v is not None}
@@ -113,15 +118,12 @@ class TeslaFleetApi:
113
118
  if json:
114
119
  json = {k: v for k, v in json.items() if v is not None}
115
120
  LOGGER.debug("Body: %s", dumps(json))
121
+ headers["Content-Type"] = "application/json"
116
122
 
117
123
  async with self.session.request(
118
124
  method,
119
125
  f"{self.server}/{path}",
120
- headers={
121
- "Authorization": f"Bearer {self.access_token}",
122
- "Content-Type": "application/json",
123
- "X-Library": f"python tesla_fleet_api {VERSION}",
124
- },
126
+ headers=headers,
125
127
  json=json,
126
128
  params=params,
127
129
  ) as resp:
@@ -45,7 +45,7 @@ class TeslaFleetOAuth(TeslaFleetApi):
45
45
  """Get the login URL."""
46
46
  if self.redirect_uri is None:
47
47
  raise ValueError("Redirect URI is missing")
48
- return f"https://auth.tesla.com/oauth2/v3/authorize?response_type=code&prompt=login&client_id={self.client_id}&redirect_uri={self.redirect_uri}&scope={'+'.join(scopes)}&state={state}"
48
+ return f"https://auth.tesla.com/oauth2/v3/authorize?response_type=code&client_id={self.client_id}&redirect_uri={self.redirect_uri}&scope={'+'.join(scopes)}&state={state}"
49
49
 
50
50
  async def get_refresh_token(self, code: str) -> None:
51
51
  """Get the refresh token."""
@@ -96,11 +96,11 @@ class Teslemetry(TeslaFleetApi):
96
96
  )
97
97
  ).get("response")
98
98
 
99
- async def vehicle_force_refresh(self, vin: str) -> dict[str, Any]:
99
+ async def vehicle_data_refresh(self, vin: str) -> dict[str, Any]:
100
100
  """Force a refresh of the vehicle data."""
101
101
  return await self._request(
102
102
  Method.GET,
103
- f"api/force/{vin}",
103
+ f"api/refresh/{vin}",
104
104
  )
105
105
 
106
106
  async def _request(
@@ -44,7 +44,7 @@ class Vehicle:
44
44
 
45
45
  def pre2021(self, vin: str) -> bool:
46
46
  """Checks if a vehicle is a pre-2021 model S or X."""
47
- return vin[9] <= "L" and vin[3] in ["S", "X"]
47
+ return vin[3] in ["S", "X"] and (vin[9] <= "L" or (vin[9] == "M" and vin[7] in ['1', '2', '3', '4']))
48
48
 
49
49
  async def actuate_trunk(
50
50
  self, vehicle_tag: str | int, which_trunk: Trunk | str
@@ -8,8 +8,11 @@ import hmac
8
8
  import hashlib
9
9
  from cryptography.hazmat.primitives.asymmetric import ec
10
10
  from cryptography.hazmat.primitives.serialization import PublicFormat, Encoding
11
+ from asyncio import Lock, sleep
11
12
 
12
- from .exceptions import MESSAGE_FAULTS, TeslaFleetMessageFaultIncorrectEpoch
13
+ from tesla_fleet_api.pb2.errors_pb2 import GenericError_E
14
+
15
+ from .exceptions import MESSAGE_FAULTS, SIGNED_MESSAGE_INFORMATION_FAULTS, TeslaFleetMessageFaultIncorrectEpoch, TeslaFleetMessageFaultInvalidTokenOrCounter
13
16
 
14
17
  from .const import (
15
18
  LOGGER,
@@ -22,19 +25,18 @@ from .const import (
22
25
  from .vehiclespecific import VehicleSpecific
23
26
 
24
27
  from .pb2.universal_message_pb2 import (
25
- # OPERATIONSTATUS_OK,
26
- MESSAGEFAULT_ERROR_INCORRECT_EPOCH,
27
28
  OPERATIONSTATUS_WAIT,
28
29
  OPERATIONSTATUS_ERROR,
29
30
  DOMAIN_VEHICLE_SECURITY,
30
31
  DOMAIN_INFOTAINMENT,
31
32
  Domain,
32
- # MessageFault_E,
33
33
  RoutableMessage,
34
34
  )
35
35
  from .pb2.car_server_pb2 import (
36
+ Response,
36
37
  Action,
37
38
  MediaPlayAction,
39
+ ResultReason,
38
40
  VehicleAction,
39
41
  VehicleControlFlashLightsAction,
40
42
  ChargingStartStopAction,
@@ -82,6 +84,8 @@ from .pb2.car_server_pb2 import (
82
84
  from .pb2.vehicle_pb2 import VehicleState
83
85
  from .pb2.vcsec_pb2 import (
84
86
  # SignedMessage_information_E,
87
+ OPERATIONSTATUS_OK,
88
+ FromVCSECMessage,
85
89
  UnsignedMessage,
86
90
  RKEAction_E,
87
91
  ClosureMoveRequest,
@@ -91,6 +95,7 @@ from .pb2.signatures_pb2 import (
91
95
  SIGNATURE_TYPE_HMAC_PERSONALIZED,
92
96
  TAG_DOMAIN,
93
97
  TAG_SIGNATURE_TYPE,
98
+ KeyIdentity,
94
99
  SignatureData,
95
100
  SessionInfo,
96
101
  HMAC_Personalized_Signature_Data,
@@ -121,35 +126,38 @@ class Session:
121
126
  epoch: bytes
122
127
  delta: int
123
128
  hmac: bytes
124
-
125
- def __init__(self, key: bytes, counter: int, epoch: bytes, delta: int):
126
- """Create a session instance for a single domain"""
127
- self.key = key
128
- self.counter = counter
129
- self.epoch = epoch
130
- self.delta = delta
129
+ publicKey: bytes
130
+ lock: Lock
131
+
132
+ def __init__(self):
133
+ self.lock = Lock()
134
+
135
+ def update(self, sessionInfo: SessionInfo, privateKey: ec.EllipticCurvePrivateKey):
136
+ """Update the session with new information"""
137
+ self.counter = sessionInfo.counter
138
+ self.epoch = sessionInfo.epoch
139
+ self.delta = int(time.time()) - sessionInfo.clock_time
140
+ self.publicKey = sessionInfo.publicKey
141
+ self.key = hashlib.sha1(
142
+ privateKey.exchange(
143
+ ec.ECDH(),
144
+ ec.EllipticCurvePublicKey.from_encoded_point(
145
+ ec.SECP256R1(), self.publicKey
146
+ ),
147
+ )
148
+ ).digest()[:16]
131
149
  self.hmac = hmac.new(
132
- key, "authenticated command".encode(), hashlib.sha256
150
+ self.key, "authenticated command".encode(), hashlib.sha256
133
151
  ).digest()
134
152
 
135
153
  def get(self) -> HMAC_Personalized_Signature_Data:
136
154
  """Sign a command and return session metadata"""
137
155
  self.counter += 1
138
- signature = HMAC_Personalized_Signature_Data()
139
- signature.epoch = self.epoch
140
- signature.counter = self.counter
141
- signature.expires_at = int(time.time()) - self.delta + 10
142
- return signature
143
-
144
- def tag(
145
- self,
146
- signature: HMAC_Personalized_Signature_Data,
147
- command: bytes,
148
- metadata: bytes,
149
- ) -> HMAC_Personalized_Signature_Data:
150
- """Sign a command and return the signature"""
151
- signature.tag = hmac.new(self.hmac, metadata + command, hashlib.sha256).digest()
152
- return signature
156
+ return HMAC_Personalized_Signature_Data(
157
+ epoch=self.epoch,
158
+ counter=self.counter,
159
+ expires_at=int(time.time()) - self.delta + 10,
160
+ )
153
161
 
154
162
 
155
163
  class VehicleSigned(VehicleSpecific):
@@ -177,22 +185,35 @@ class VehicleSigned(VehicleSpecific):
177
185
  self._from_destination = randbytes(16)
178
186
  self._sessions = {}
179
187
 
180
- async def _signed_message(self, msg: RoutableMessage) -> RoutableMessage:
188
+ async def _send(self, msg: RoutableMessage) -> RoutableMessage:
181
189
  """Serialize a message and send to the signed command endpoint."""
182
- routable_message = base64.b64encode(msg.SerializeToString()).decode()
183
- resp_json = await self.signed_command(routable_message)
184
- resp = RoutableMessage()
185
- resp.ParseFromString(base64.b64decode(resp_json["response"]))
186
190
 
187
- if resp.signedMessageStatus:
188
- LOGGER.error("Command returned with signedMessageStatus: %s", resp.signedMessageStatus)
189
- if resp.signedMessageStatus.operation_status == OPERATIONSTATUS_ERROR:
190
- raise MESSAGE_FAULTS[resp.signedMessageStatus.signed_message_fault]
191
+ async with self._sessions[msg.to_destination.domain].lock:
192
+ resp = await self.signed_command(
193
+ base64.b64encode(msg.SerializeToString()).decode()
194
+ )
195
+
196
+ resp_msg = RoutableMessage.FromString(base64.b64decode(resp["response"]))
191
197
 
192
- return resp
198
+ # Check UUID?
199
+ # Check RoutingAdress?
193
200
 
194
- async def _handshake(self, domain: int) -> None:
201
+ if resp_msg.session_info:
202
+ self._sessions[resp_msg.from_destination.domain].update(
203
+ SessionInfo.FromString(resp_msg.session_info), self.private_key
204
+ )
205
+
206
+ if resp_msg.signedMessageStatus.signed_message_fault:
207
+ raise MESSAGE_FAULTS[resp_msg.signedMessageStatus.signed_message_fault]
208
+
209
+ return resp_msg
210
+
211
+ async def _handshake(self, domain: Domain) -> Session:
195
212
  """Perform a handshake with the vehicle."""
213
+ if session := self._sessions.get(domain):
214
+ return session
215
+ self._sessions[domain] = Session()
216
+
196
217
  LOGGER.debug(f"Handshake with domain {Domain.Name(domain)}")
197
218
  msg = RoutableMessage()
198
219
  msg.to_destination.domain = domain
@@ -201,51 +222,34 @@ class VehicleSigned(VehicleSpecific):
201
222
  msg.uuid = randbytes(16)
202
223
 
203
224
  # Send handshake message
204
- resp = await self._signed_message(msg)
205
-
206
- # Get session info with publicKey, epoch, and clock_time
207
- info = SessionInfo.FromString(resp.session_info)
225
+ await self._send(msg)
208
226
 
209
- vehicle_public_key = info.publicKey
210
-
211
- # Derive shared key from private key _key and vehicle public key
212
- shared = self.private_key.exchange(
213
- ec.ECDH(),
214
- ec.EllipticCurvePublicKey.from_encoded_point(
215
- ec.SECP256R1(), vehicle_public_key
216
- ),
217
- )
218
-
219
- self._sessions[domain] = Session(
220
- key=hashlib.sha1(shared).digest()[:16],
221
- counter=info.counter,
222
- epoch=info.epoch,
223
- delta=int(time.time()) - info.clock_time,
224
- )
227
+ return self._sessions[domain]
225
228
 
226
229
  async def _sendVehicleSecurity(self, command: UnsignedMessage) -> dict[str, Any]:
227
230
  """Sign and send a message to Infotainment computer."""
228
- if DOMAIN_VEHICLE_SECURITY not in self._sessions:
229
- await self._handshake(DOMAIN_VEHICLE_SECURITY)
230
- return await self._send(DOMAIN_VEHICLE_SECURITY, command.SerializeToString())
231
+ return await self._sign(DOMAIN_VEHICLE_SECURITY, command.SerializeToString())
231
232
 
232
233
  async def _sendInfotainment(self, command: Action) -> dict[str, Any]:
233
234
  """Sign and send a message to Infotainment computer."""
234
- if DOMAIN_INFOTAINMENT not in self._sessions:
235
- await self._handshake(DOMAIN_INFOTAINMENT)
236
- return await self._send(DOMAIN_INFOTAINMENT, command.SerializeToString())
235
+ return await self._sign(DOMAIN_INFOTAINMENT, command.SerializeToString())
237
236
 
238
- async def _send(self, domain: int, command: bytes, attempt: int = 1) -> dict[str, Any]:
237
+ async def _sign(
238
+ self, domain: Domain, command: bytes, attempt: int = 1
239
+ ) -> dict[str, Any]:
239
240
  """Send a signed message to the vehicle."""
240
241
  LOGGER.debug(f"Sending to domain {Domain.Name(domain)}")
242
+
243
+ session = await self._handshake(domain)
244
+ hmac_personalized = session.get()
245
+
241
246
  msg = RoutableMessage()
242
247
  msg.to_destination.domain = domain
243
248
  msg.from_destination.routing_address = self._from_destination
244
249
  msg.protobuf_message_as_bytes = command
245
250
  msg.uuid = randbytes(16)
246
251
 
247
- session = self._sessions[domain].get()
248
- metadata = [
252
+ metadata = bytes([
249
253
  TAG_SIGNATURE_TYPE,
250
254
  1,
251
255
  SIGNATURE_TYPE_HMAC_PERSONALIZED,
@@ -256,48 +260,92 @@ class VehicleSigned(VehicleSpecific):
256
260
  17,
257
261
  *self.vin.encode(),
258
262
  TAG_EPOCH,
259
- len(session.epoch),
260
- *session.epoch,
263
+ len(hmac_personalized.epoch),
264
+ *hmac_personalized.epoch,
261
265
  TAG_EXPIRES_AT,
262
266
  4,
263
- *struct.pack(">I", session.expires_at),
267
+ *struct.pack(">I", hmac_personalized.expires_at),
264
268
  TAG_COUNTER,
265
269
  4,
266
- *struct.pack(">I", session.counter),
270
+ *struct.pack(">I", hmac_personalized.counter),
267
271
  TAG_END,
268
- ]
269
-
270
- session = self._sessions[domain].tag(session, command, bytes(metadata))
272
+ ])
271
273
 
272
- signature = SignatureData()
273
- signature.HMAC_Personalized_data.CopyFrom(session)
274
- signature.signer_identity.public_key = self._public_key
274
+ hmac_personalized.tag = hmac.new(
275
+ session.hmac, metadata + command, hashlib.sha256
276
+ ).digest()
275
277
 
276
- msg.signature_data.CopyFrom(signature)
278
+ # I think this whole section could be improved
279
+ msg.signature_data.HMAC_Personalized_data.CopyFrom(hmac_personalized)
280
+ msg.signature_data.signer_identity.public_key = self._public_key
277
281
 
278
282
  try:
279
- resp = await self._signed_message(msg)
280
- except TeslaFleetMessageFaultIncorrectEpoch as e:
283
+ resp = await self._send(msg)
284
+ except (
285
+ TeslaFleetMessageFaultIncorrectEpoch,
286
+ TeslaFleetMessageFaultInvalidTokenOrCounter,
287
+ ) as e:
281
288
  attempt += 1
282
289
  if attempt > 3:
283
290
  # We tried 3 times, give up, raise the error
284
291
  raise e
285
- LOGGER.info(f"Session expired, starting new handshake with {Domain.Name(domain)}")
286
- await self._handshake(domain)
287
- LOGGER.info(f"Handshake complete, retrying message to {Domain.Name(domain)}")
288
- return await self._send(domain, command, attempt)
292
+ return await self._sign(domain, command, attempt)
289
293
 
290
294
  if resp.signedMessageStatus.operation_status == OPERATIONSTATUS_WAIT:
291
- return {"response": {"result": False}}
292
-
293
- LOGGER.debug("Command response: %s", resp)
294
- reason = resp.protobuf_message_as_bytes[8:].decode()
295
-
296
- if reason:
297
- LOGGER.error("Command failed with reason: %s", reason)
298
- return {"response": {"result": False, "reason": reason}}
299
-
300
- return {"response": {"result": True, "reason": ""}}
295
+ attempt += 1
296
+ if attempt > 3:
297
+ # We tried 3 times, give up, raise the error
298
+ return {"response": {"result": False, "reason": "Too many retries"}}
299
+ async with session.lock:
300
+ await sleep(2)
301
+ return await self._sign(domain, command, attempt)
302
+
303
+ if resp.HasField("signedMessageStatus"):
304
+ if(resp.from_destination.domain == DOMAIN_VEHICLE_SECURITY):
305
+ vcsec = FromVCSECMessage.FromString(resp.protobuf_message_as_bytes)
306
+ LOGGER.debug("VCSEC Response: %s", vcsec)
307
+ if vcsec.HasField("nominalError"):
308
+ LOGGER.error("Command failed with reason: %s", vcsec.nominalError.genericError)
309
+ return {
310
+ "response": {
311
+ "result": False,
312
+ "reason": GenericError_E.Name(vcsec.nominalError.genericError)
313
+ }
314
+ }
315
+ elif vcsec.commandStatus.operationStatus == OPERATIONSTATUS_OK:
316
+ return {"response": {"result": True, "reason": ""}}
317
+ elif vcsec.commandStatus.operationStatus == OPERATIONSTATUS_WAIT:
318
+ attempt += 1
319
+ if attempt > 3:
320
+ # We tried 3 times, give up, raise the error
321
+ return {"response": {"result": False, "reason": "Too many retries"}}
322
+ async with session.lock:
323
+ await sleep(2)
324
+ return await self._sign(domain, command, attempt)
325
+ elif vcsec.commandStatus.operationStatus == OPERATIONSTATUS_ERROR:
326
+ if(vcsec.commandStatus.signedMessageStatus):
327
+ raise SIGNED_MESSAGE_INFORMATION_FAULTS[vcsec.commandStatus.signedMessageStatus.signedMessageInformation]
328
+
329
+ elif(resp.from_destination.domain == DOMAIN_INFOTAINMENT):
330
+ response = Response.FromString(resp.protobuf_message_as_bytes)
331
+ LOGGER.debug("Infotainment Response: %s", response)
332
+ if (response.HasField("ping")):
333
+ print(response.ping)
334
+ return {
335
+ "response": {
336
+ "result": True,
337
+ "reason": response.ping.local_timestamp
338
+ }
339
+ }
340
+ if response.HasField("actionStatus"):
341
+ return {
342
+ "response": {
343
+ "result": response.actionStatus.result == OPERATIONSTATUS_OK,
344
+ "reason": response.actionStatus.result_reason.plain_text or ""
345
+ }
346
+ }
347
+
348
+ return {"response": {"result": None, "reason": "No Response"}}
301
349
 
302
350
  async def actuate_trunk(self, which_trunk: Trunk | str) -> dict[str, Any]:
303
351
  """Controls the front or rear trunk."""
@@ -1,8 +1,8 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: tesla_fleet_api
3
- Version: 0.9.5
3
+ Version: 0.9.6
4
4
  Summary: Tesla Fleet API library for Python
5
- Home-page: https://github.com/Teslemetry/tesla_fleet_api
5
+ Home-page: https://github.com/Teslemetry/python-tesla-fleet-api
6
6
  Author: Brett Adams
7
7
  Author-email: hello@teslemetry.com
8
8
  Classifier: Development Status :: 4 - Beta
@@ -17,6 +17,15 @@ Requires-Dist: aiofiles
17
17
  Requires-Dist: aiolimiter
18
18
  Requires-Dist: cryptography
19
19
  Requires-Dist: protobuf
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: requires-dist
27
+ Dynamic: requires-python
28
+ Dynamic: summary
20
29
 
21
30
  # Tesla Fleet Api
22
31
  Python library for Tesla Fleet API and Teslemetry.
@@ -0,0 +1,42 @@
1
+ tesla_fleet_api/__init__.py,sha256=BVZUDsfaxT05tAfcMHHWiyFyXwmDOx_wP_IHZBscgho,729
2
+ tesla_fleet_api/charging.py,sha256=N_mc8axrXj3iduqLj_jCt4Vx86tHqe3xqQT4R1R7HvU,1689
3
+ tesla_fleet_api/const.py,sha256=UdgXLx2blO4ByR3oY0YP2V0LjBCYbhsmWuMkBr3gwV0,12888
4
+ tesla_fleet_api/energy.py,sha256=S7D75MPuMVsHgkyUcFfMqjGCLZBM5YVFlWLEHbaX-zw,5957
5
+ tesla_fleet_api/energyspecific.py,sha256=UfeaGE59aoAa8UhpQCXUi0sOrNCA40xZlqwF73BXTVY,4254
6
+ tesla_fleet_api/exceptions.py,sha256=qnRWqPJ_5gia4-j3o4mP5OwUuBRtC3SAbZKo-_XSRiI,29729
7
+ tesla_fleet_api/partner.py,sha256=1vIBUaxKLIfqcC0X6VXZN0dMAzj_CLNPUMjA6QVqZ1k,1223
8
+ tesla_fleet_api/ratecalculator.py,sha256=4lz8yruUeouHXh_3ezsXX-CTpIegp1T1J4VuRV_qdHA,1791
9
+ tesla_fleet_api/teslafleetapi.py,sha256=8j-kvV6_oCuIlDdS5kWlCbo9chWsT48Gn2iKt3uOAjg,7409
10
+ tesla_fleet_api/teslafleetoauth.py,sha256=NmzycAeg68sWASjiWan3B5LdIgrfmoLTDqLHYCmoKKg,4108
11
+ tesla_fleet_api/teslafleetopensource.py,sha256=TJfVPcqJlA1b3kMoGuLr-g5Gn8UDyYsTZhjvGY1MtIk,2007
12
+ tesla_fleet_api/teslemetry.py,sha256=_n59RQvJKl82ylLe09bLY_8iyfjz_DHqCdRVsWeif4s,3308
13
+ tesla_fleet_api/tessie.py,sha256=4dBYxe1G2v9JvJGRbb01wXrAmvWT4jOfV4f_VQE_vkE,2302
14
+ tesla_fleet_api/user.py,sha256=TZE2oh-n5zrhKXmGRuiNL9voKVODD7rBhGE_IObYVGA,1179
15
+ tesla_fleet_api/vehicle.py,sha256=mSFJG-bLBNh_iSJnruk9EZSypZUxMpKwMgKA9_8CPDc,32146
16
+ tesla_fleet_api/vehiclesigned.py,sha256=N06Evfo5_AwkOjNg0cpCHYV7SH-5zjzHsYnMmYpCKZY,42001
17
+ tesla_fleet_api/vehiclespecific.py,sha256=P7KI8MqUbAyM2cfDC8NqbJXzGL2ZsOIx3IBeqB8xYQY,20656
18
+ tesla_fleet_api/pb2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ tesla_fleet_api/pb2/__init__.pyi,sha256=qFXWNIgl71wB260u-XPzaAwWAHL6krw21q-aXnBtop0,252
20
+ tesla_fleet_api/pb2/car_server_pb2.py,sha256=v_eb4NDIkx_ZYPYW29_EFRky5vQ4b2q14gwuQSbouYw,29202
21
+ tesla_fleet_api/pb2/car_server_pb2.pyi,sha256=qkig9HEsOE4Dk2-r38BAWOyALqt7CtUnlirgSVt_oa8,46334
22
+ tesla_fleet_api/pb2/common_pb2.py,sha256=C5O6BBTckU3F-XItLfivaPG_XVCnzF_JX4mpVfM_jFk,3931
23
+ tesla_fleet_api/pb2/common_pb2.pyi,sha256=8aZiigp74MfSOMpHtMw0D9Jj0kwPz1zV_BIZfVWhh_0,5012
24
+ tesla_fleet_api/pb2/errors_pb2.py,sha256=nfOvriyjVgVxnJZIAYNRHiBa1_14dOXhC9Pokc5anV8,1631
25
+ tesla_fleet_api/pb2/errors_pb2.pyi,sha256=CNsAR7Oe6wavnqjzhInEyAFxMio6TtXfaNT0DUztn6o,1478
26
+ tesla_fleet_api/pb2/keys_pb2.py,sha256=-hyVP9-W1DypTYLaWwkOSUzgia6l9R3M9wUIgvNs-T0,1252
27
+ tesla_fleet_api/pb2/keys_pb2.pyi,sha256=HH-TfhE5ihwmoPCGdiVnZ5B7KkaMJglEpusgLc1J02M,676
28
+ tesla_fleet_api/pb2/managed_charging_pb2.py,sha256=4crkLo6uC0YPkgw90jRjAqlGMFnRze_koUt2-pw14m4,1430
29
+ tesla_fleet_api/pb2/managed_charging_pb2.pyi,sha256=SqEmrfknTfzYTUYHtsoCpt1Fw2YpU3OyQllX247UfyQ,1227
30
+ tesla_fleet_api/pb2/signatures_pb2.py,sha256=TRaJ6lzmJtryhhmBC_PbYzftc-pqCmwC6wuBCXHeuTg,4734
31
+ tesla_fleet_api/pb2/signatures_pb2.pyi,sha256=-OGm9ctBnEJiXk-3nkFCPRTxKgFiqFgMbKeq3x2zGGM,6101
32
+ tesla_fleet_api/pb2/universal_message_pb2.py,sha256=UklH73qoYsqxylie6IK8iIcw2tmykSqDiaBKSWz66OQ,5093
33
+ tesla_fleet_api/pb2/universal_message_pb2.pyi,sha256=a_RygTJL26v0rA7QuUwsxiG1_ZBcliwXCqOAqTIJ6UE,7647
34
+ tesla_fleet_api/pb2/vcsec_pb2.py,sha256=PDv9TfiXnNs6sQ0D5vBrsSSPinSqu3eBUwvTcG8xMWo,15919
35
+ tesla_fleet_api/pb2/vcsec_pb2.pyi,sha256=cyK1uyRtDjRVqVlyl5uRQYY1RhFlWSJheLg3PGfs-_s,28524
36
+ tesla_fleet_api/pb2/vehicle_pb2.py,sha256=bqyFJM-1qZ7W9XKREINhYZx8yXAudmq6W8_Pdfkhbkk,44711
37
+ tesla_fleet_api/pb2/vehicle_pb2.pyi,sha256=sAUW_9aVB8NqJCnhZjXMLfqfePLVZv_7PfSKZKEBaQA,74251
38
+ tesla_fleet_api-0.9.6.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
39
+ tesla_fleet_api-0.9.6.dist-info/METADATA,sha256=NZZXKNqQjFt6LPS7r8TOsnEH89osm5SqRcVaIvaB5Ck,4022
40
+ tesla_fleet_api-0.9.6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
41
+ tesla_fleet_api-0.9.6.dist-info/top_level.txt,sha256=jeNbog_1saXBFrGpom9WyPWmilxsyP3szL_G7JLWQfM,16
42
+ tesla_fleet_api-0.9.6.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.7.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,33 +0,0 @@
1
- tesla_fleet_api/__init__.py,sha256=BVZUDsfaxT05tAfcMHHWiyFyXwmDOx_wP_IHZBscgho,729
2
- tesla_fleet_api/charging.py,sha256=N_mc8axrXj3iduqLj_jCt4Vx86tHqe3xqQT4R1R7HvU,1689
3
- tesla_fleet_api/const.py,sha256=40Oi7XwilcT6ZbNcQnm9hOawKNY4JLrfZA0Xc4H7g7s,12888
4
- tesla_fleet_api/energy.py,sha256=S7D75MPuMVsHgkyUcFfMqjGCLZBM5YVFlWLEHbaX-zw,5957
5
- tesla_fleet_api/energyspecific.py,sha256=UfeaGE59aoAa8UhpQCXUi0sOrNCA40xZlqwF73BXTVY,4254
6
- tesla_fleet_api/exceptions.py,sha256=KUozIEvZv9Haal6ofRb0SFHQQBe8UH-lEAFVwkJFzW0,20942
7
- tesla_fleet_api/partner.py,sha256=1vIBUaxKLIfqcC0X6VXZN0dMAzj_CLNPUMjA6QVqZ1k,1223
8
- tesla_fleet_api/ratecalculator.py,sha256=4lz8yruUeouHXh_3ezsXX-CTpIegp1T1J4VuRV_qdHA,1791
9
- tesla_fleet_api/teslafleetapi.py,sha256=4Rnh_HNZkIw54o9SYAaHsA8vY0pmk_bMncrLuJ8MtSk,7389
10
- tesla_fleet_api/teslafleetoauth.py,sha256=ClrVh4_lpatW8w44fWM0PZiVB-ciPHr-9h4yw1Zf9w8,4121
11
- tesla_fleet_api/teslafleetopensource.py,sha256=TJfVPcqJlA1b3kMoGuLr-g5Gn8UDyYsTZhjvGY1MtIk,2007
12
- tesla_fleet_api/teslemetry.py,sha256=IiDxEjDfWdXlpI7qFGW2YwroWoLbUdWSSVM6zCnhQl8,3307
13
- tesla_fleet_api/tessie.py,sha256=4dBYxe1G2v9JvJGRbb01wXrAmvWT4jOfV4f_VQE_vkE,2302
14
- tesla_fleet_api/user.py,sha256=TZE2oh-n5zrhKXmGRuiNL9voKVODD7rBhGE_IObYVGA,1179
15
- tesla_fleet_api/vehicle.py,sha256=e0AotkaI-WftNfxJbLF8peIq3l99f6-ezVSEQbkOD1I,32090
16
- tesla_fleet_api/vehiclesigned.py,sha256=PXXBeMpQf_5-ZQccjhtf6wllgCMhOOmtSjZzmjHYgls,39886
17
- tesla_fleet_api/vehiclespecific.py,sha256=P7KI8MqUbAyM2cfDC8NqbJXzGL2ZsOIx3IBeqB8xYQY,20656
18
- tesla_fleet_api/pb2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- tesla_fleet_api/pb2/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- tesla_fleet_api/pb2/car_server_pb2.py,sha256=I3bq-cbq_4GXZblJatOFtjUny9seZb_1caNWMUciry0,51750
21
- tesla_fleet_api/pb2/common_pb2.py,sha256=Iip_uomlO1s6wTyQl2QYxkUdVKB1qqSrpD3MHr-5rl0,6041
22
- tesla_fleet_api/pb2/errors_pb2.py,sha256=ESk8GRN-jbsVvPWsWC0WfntmOXfRWVjy3sPT99WL89Q,2149
23
- tesla_fleet_api/pb2/keys_pb2.py,sha256=T5q0OMlMiwFJV5xSmn6E2tnLCzr463Rk3pweaLFiPgM,1366
24
- tesla_fleet_api/pb2/managed_charging_pb2.py,sha256=g6dJu9sYe9D2BCAWNFUNM_Qr0gJM5AHxmAJikHstQMc,1792
25
- tesla_fleet_api/pb2/signatures_pb2.py,sha256=UZCXDYgV-tYIrzVHeqi0NaytUtRZJB-_lC7IVjIbggc,7719
26
- tesla_fleet_api/pb2/universal_message_pb2.py,sha256=XF08dOMbhN0F9D4xbaNJhUIAEqbKdK1iYQdZFAloWdk,7826
27
- tesla_fleet_api/pb2/vcsec_pb2.py,sha256=CMJ97e4Mm4p7NFcgybbCC2KJRcvtrcqmBy_pGycYhMo,26238
28
- tesla_fleet_api/pb2/vehicle_pb2.py,sha256=ViSm74kS3iLjB2C18w-FDZyCkvi2e71GNKVmH2FU8zg,57629
29
- tesla_fleet_api-0.9.5.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
30
- tesla_fleet_api-0.9.5.dist-info/METADATA,sha256=f08KYxa2kQw_SuEZ0lAir5VORsuA95XYMVMmRl-80hE,3818
31
- tesla_fleet_api-0.9.5.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
32
- tesla_fleet_api-0.9.5.dist-info/top_level.txt,sha256=jeNbog_1saXBFrGpom9WyPWmilxsyP3szL_G7JLWQfM,16
33
- tesla_fleet_api-0.9.5.dist-info/RECORD,,