tesla-fleet-api 0.9.5__py3-none-any.whl → 0.9.6__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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,,