tesla-fleet-api 1.0.1__py3-none-any.whl → 1.0.3__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.
@@ -13,16 +13,19 @@ from cryptography.hazmat.primitives.hashes import Hash, SHA256
13
13
  from cryptography.hazmat.primitives.ciphers.aead import AESGCM
14
14
  from asyncio import Lock, sleep
15
15
 
16
- from ...exceptions import (
16
+ from tesla_fleet_api.exceptions import (
17
+ MESSAGE_FAULTS,
17
18
  SIGNED_MESSAGE_INFORMATION_FAULTS,
19
+ NotOnWhitelistFault,
20
+ #TeslaFleetMessageFaultInvalidSignature,
18
21
  TeslaFleetMessageFaultIncorrectEpoch,
19
22
  TeslaFleetMessageFaultInvalidTokenOrCounter,
20
23
  )
21
24
 
22
- from .vehicle import Vehicle
25
+ from tesla_fleet_api.tesla.vehicle.vehicle import Vehicle
23
26
 
24
27
 
25
- from ...const import (
28
+ from tesla_fleet_api.const import (
26
29
  LOGGER,
27
30
  Trunk,
28
31
  ClimateKeeperMode,
@@ -32,40 +35,32 @@ from ...const import (
32
35
  )
33
36
 
34
37
  # Protocol
35
- from .proto.errors_pb2 import GenericError_E
36
- from .proto.car_server_pb2 import (
38
+ from tesla_fleet_api.tesla.vehicle.proto.errors_pb2 import GenericError_E
39
+ from tesla_fleet_api.tesla.vehicle.proto.car_server_pb2 import (
37
40
  Response,
38
41
  )
39
- from .proto.signatures_pb2 import (
40
- SIGNATURE_TYPE_AES_GCM_PERSONALIZED,
41
- SIGNATURE_TYPE_HMAC_PERSONALIZED,
42
- TAG_COUNTER,
43
- TAG_DOMAIN,
44
- TAG_END,
45
- TAG_EPOCH,
46
- TAG_EXPIRES_AT,
47
- TAG_PERSONALIZATION,
48
- TAG_SIGNATURE_TYPE,
42
+ from tesla_fleet_api.tesla.vehicle.proto.signatures_pb2 import (
43
+ Session_Info_Status,
44
+ SignatureType,
45
+ Tag,
49
46
  AES_GCM_Personalized_Signature_Data,
50
47
  KeyIdentity,
51
48
  SessionInfo,
52
49
  SignatureData,
53
50
  )
54
- from .proto.universal_message_pb2 import (
55
- DOMAIN_INFOTAINMENT,
56
- DOMAIN_VEHICLE_SECURITY,
57
- OPERATIONSTATUS_ERROR,
58
- OPERATIONSTATUS_WAIT,
51
+ from tesla_fleet_api.tesla.vehicle.proto.universal_message_pb2 import (
52
+ OperationStatus_E,
59
53
  Destination,
60
54
  Domain,
61
55
  RoutableMessage,
62
56
  SessionInfoRequest,
57
+ Flags,
63
58
  )
64
- from .proto.vcsec_pb2 import (
65
- OPERATIONSTATUS_OK,
59
+ from tesla_fleet_api.tesla.vehicle.proto.vcsec_pb2 import (
66
60
  FromVCSECMessage,
61
+ VehicleStatus,
67
62
  )
68
- from .proto.car_server_pb2 import (
63
+ from tesla_fleet_api.tesla.vehicle.proto.car_server_pb2 import (
69
64
  Action,
70
65
  MediaPlayAction,
71
66
  VehicleAction,
@@ -83,8 +78,6 @@ from .proto.car_server_pb2 import (
83
78
  HvacSteeringWheelHeaterAction,
84
79
  HvacTemperatureAdjustmentAction,
85
80
  GetNearbyChargingSites,
86
- # NearbyChargingSites,
87
- # Superchargers,
88
81
  VehicleControlCancelSoftwareUpdateAction,
89
82
  VehicleControlHonkHornAction,
90
83
  VehicleControlResetValetPinAction,
@@ -112,24 +105,24 @@ from .proto.car_server_pb2 import (
112
105
  MediaPreviousTrack,
113
106
  MediaPreviousFavorite,
114
107
  )
115
- from .proto.vehicle_pb2 import VehicleState, ClimateState
116
- from .proto.vcsec_pb2 import (
108
+ from tesla_fleet_api.tesla.vehicle.proto.vehicle_pb2 import VehicleData, VehicleState, ClimateState
109
+ from tesla_fleet_api.tesla.vehicle.proto.vcsec_pb2 import (
117
110
  UnsignedMessage,
118
111
  RKEAction_E,
119
112
  ClosureMoveRequest,
120
113
  ClosureMoveType_E,
121
114
  )
122
- from .proto.signatures_pb2 import (
115
+ from tesla_fleet_api.tesla.vehicle.proto.signatures_pb2 import (
123
116
  HMAC_Personalized_Signature_Data,
124
117
  )
125
- from .proto.common_pb2 import (
118
+ from tesla_fleet_api.tesla.vehicle.proto.common_pb2 import (
126
119
  Void,
127
120
  PreconditioningTimes,
128
121
  OffPeakChargingTimes,
129
122
  )
130
123
 
131
124
  if TYPE_CHECKING:
132
- from ..tesla import Tesla
125
+ from tesla_fleet_api.tesla.tesla import Tesla
133
126
 
134
127
  # ENUMs to convert ints to proto typed ints
135
128
  AutoSeatClimatePositions = (
@@ -175,6 +168,7 @@ class Session:
175
168
  hmac: bytes
176
169
  publicKey: bytes
177
170
  lock: Lock
171
+ ready: bool
178
172
 
179
173
  def __init__(self, parent: Commands, domain: Domain):
180
174
  self.parent = parent
@@ -260,10 +254,21 @@ class Commands(Vehicle):
260
254
 
261
255
 
262
256
  @abstractmethod
263
- async def _send(self, msg: RoutableMessage) -> RoutableMessage:
257
+ async def _send(self, msg: RoutableMessage, requires: str) -> RoutableMessage:
264
258
  """Transmit the message to the vehicle."""
265
259
  raise NotImplementedError
266
260
 
261
+ def validate_msg(self, msg: RoutableMessage) -> None:
262
+ """Validate the message."""
263
+ if(msg.session_info):
264
+ info = SessionInfo.FromString(msg.session_info)
265
+ if(info.status == Session_Info_Status.SESSION_INFO_STATUS_KEY_NOT_ON_WHITELIST):
266
+ raise NotOnWhitelistFault
267
+ self._sessions[msg.from_destination.domain].update(info)
268
+
269
+ if msg.signedMessageStatus.signed_message_fault > 0:
270
+ raise MESSAGE_FAULTS[msg.signedMessageStatus.signed_message_fault]
271
+
267
272
  @abstractmethod
268
273
  async def _command(self, domain: Domain, command: bytes, attempt: int = 0) -> dict[str, Any]:
269
274
  """Serialize a message and send to the signed command endpoint."""
@@ -279,8 +284,9 @@ class Commands(Vehicle):
279
284
  raise ValueError(f"Unknown auth method: {self._auth_method}")
280
285
 
281
286
  try:
282
- resp = await self._send(msg)
287
+ resp = await self._send(msg, "protobuf_message_as_bytes")
283
288
  except (
289
+ #TeslaFleetMessageFaultInvalidSignature,
284
290
  TeslaFleetMessageFaultIncorrectEpoch,
285
291
  TeslaFleetMessageFaultInvalidTokenOrCounter,
286
292
  ) as e:
@@ -290,7 +296,7 @@ class Commands(Vehicle):
290
296
  raise e
291
297
  return await self._command(domain, command, attempt)
292
298
 
293
- if resp.signedMessageStatus.operation_status == OPERATIONSTATUS_WAIT:
299
+ if resp.signedMessageStatus.operation_status == OperationStatus_E.OPERATIONSTATUS_WAIT:
294
300
  attempt += 1
295
301
  if attempt > 3:
296
302
  # We tried 3 times, give up, raise the error
@@ -300,8 +306,53 @@ class Commands(Vehicle):
300
306
  return await self._command(domain, command, attempt)
301
307
 
302
308
  if resp.HasField("protobuf_message_as_bytes"):
303
- if(resp.from_destination.domain == DOMAIN_VEHICLE_SECURITY):
304
- vcsec = FromVCSECMessage.FromString(resp.protobuf_message_as_bytes)
309
+ #decrypt
310
+ if(resp.signature_data.HasField("AES_GCM_Response_data")):
311
+ if(msg.signature_data.HasField("AES_GCM_Personalized_data")):
312
+ request_hash = bytes([SignatureType.SIGNATURE_TYPE_AES_GCM_PERSONALIZED]) + msg.signature_data.AES_GCM_Personalized_data.tag
313
+ elif(msg.signature_data.HasField("HMAC_Personalized_data")):
314
+ request_hash = bytes([SignatureType.SIGNATURE_TYPE_HMAC_PERSONALIZED]) + msg.signature_data.HMAC_Personalized_data.tag
315
+ if(session.domain == Domain.DOMAIN_VEHICLE_SECURITY):
316
+ request_hash = request_hash[:17]
317
+ else:
318
+ raise ValueError("Invalid request signature data")
319
+
320
+ metadata = bytes([
321
+ Tag.TAG_SIGNATURE_TYPE,
322
+ 1,
323
+ SignatureType.SIGNATURE_TYPE_AES_GCM_RESPONSE,
324
+ Tag.TAG_DOMAIN,
325
+ 1,
326
+ resp.from_destination.domain,
327
+ Tag.TAG_PERSONALIZATION,
328
+ 17,
329
+ *self.vin.encode(),
330
+ Tag.TAG_COUNTER,
331
+ 4,
332
+ *struct.pack(">I", resp.signature_data.AES_GCM_Response_data.counter),
333
+ Tag.TAG_FLAGS,
334
+ 4,
335
+ *struct.pack(">I", resp.flags),
336
+ Tag.TAG_REQUEST_HASH,
337
+ 17,
338
+ *request_hash,
339
+ Tag.TAG_FAULT,
340
+ 4,
341
+ *struct.pack(">I", resp.signedMessageStatus.signed_message_fault),
342
+ Tag.TAG_END,
343
+ ])
344
+
345
+ aad = Hash(SHA256())
346
+ aad.update(metadata)
347
+ aesgcm = AESGCM(session.sharedKey)
348
+ resp.protobuf_message_as_bytes = aesgcm.decrypt(resp.signature_data.AES_GCM_Response_data.nonce, resp.protobuf_message_as_bytes + resp.signature_data.AES_GCM_Response_data.tag, aad.finalize())
349
+
350
+ if(resp.from_destination.domain == Domain.DOMAIN_VEHICLE_SECURITY):
351
+ try:
352
+ vcsec = FromVCSECMessage.FromString(resp.protobuf_message_as_bytes)
353
+ except Exception as e:
354
+ LOGGER.error("Failed to parse VCSEC message: %s %s", e, resp)
355
+ raise e
305
356
  LOGGER.debug("VCSEC Response: %s", vcsec)
306
357
  if vcsec.HasField("nominalError"):
307
358
  LOGGER.error("Command failed with reason: %s", vcsec.nominalError.genericError)
@@ -311,9 +362,13 @@ class Commands(Vehicle):
311
362
  "reason": GenericError_E.Name(vcsec.nominalError.genericError)
312
363
  }
313
364
  }
314
- elif vcsec.commandStatus.operationStatus == OPERATIONSTATUS_OK:
365
+ elif vcsec.HasField("vehicleStatus"):
366
+ return {
367
+ "response": vcsec.vehicleStatus
368
+ }
369
+ elif vcsec.commandStatus.operationStatus == OperationStatus_E.OPERATIONSTATUS_OK:
315
370
  return {"response": {"result": True, "reason": ""}}
316
- elif vcsec.commandStatus.operationStatus == OPERATIONSTATUS_WAIT:
371
+ elif vcsec.commandStatus.operationStatus == OperationStatus_E.OPERATIONSTATUS_WAIT:
317
372
  attempt += 1
318
373
  if attempt > 3:
319
374
  # We tried 3 times, give up, raise the error
@@ -321,29 +376,37 @@ class Commands(Vehicle):
321
376
  async with session.lock:
322
377
  await sleep(2)
323
378
  return await self._command(domain, command, attempt)
324
- elif vcsec.commandStatus.operationStatus == OPERATIONSTATUS_ERROR:
379
+ elif vcsec.commandStatus.operationStatus == OperationStatus_E.OPERATIONSTATUS_ERROR:
325
380
  if(resp.HasField("signedMessageStatus")):
326
381
  raise SIGNED_MESSAGE_INFORMATION_FAULTS[vcsec.commandStatus.signedMessageStatus.signedMessageInformation]
327
382
 
328
- elif(resp.from_destination.domain == DOMAIN_INFOTAINMENT):
329
- response = Response.FromString(resp.protobuf_message_as_bytes)
383
+ elif(resp.from_destination.domain == Domain.DOMAIN_INFOTAINMENT):
384
+ try:
385
+ response = Response.FromString(resp.protobuf_message_as_bytes)
386
+ except Exception as e:
387
+ LOGGER.error("Failed to parse Infotainment Response: %s %s", e, resp)
388
+ raise e
330
389
  LOGGER.debug("Infotainment Response: %s", response)
331
390
  if (response.HasField("ping")):
332
- print(response.ping)
333
391
  return {
334
392
  "response": {
335
393
  "result": True,
336
394
  "reason": response.ping.local_timestamp
337
395
  }
338
396
  }
397
+ if response.HasField("vehicleData"):
398
+ return {
399
+ "response": response.vehicleData
400
+ }
339
401
  if response.HasField("actionStatus"):
340
402
  return {
341
403
  "response": {
342
- "result": response.actionStatus.result == OPERATIONSTATUS_OK,
404
+ "result": response.actionStatus.result == OperationStatus_E.OPERATIONSTATUS_OK,
343
405
  "reason": response.actionStatus.result_reason.plain_text or ""
344
406
  }
345
407
  }
346
408
 
409
+
347
410
  return {"response": {"result": True, "reason": ""}}
348
411
 
349
412
  async def _commandHmac(self, session: Session, command: bytes, attempt: int = 1) -> RoutableMessage:
@@ -353,25 +416,25 @@ class Commands(Vehicle):
353
416
  hmac_personalized = session.hmac_personalized()
354
417
 
355
418
  metadata = bytes([
356
- TAG_SIGNATURE_TYPE,
419
+ Tag.TAG_SIGNATURE_TYPE,
357
420
  1,
358
- SIGNATURE_TYPE_HMAC_PERSONALIZED,
359
- TAG_DOMAIN,
421
+ SignatureType.SIGNATURE_TYPE_HMAC_PERSONALIZED,
422
+ Tag.TAG_DOMAIN,
360
423
  1,
361
424
  session.domain,
362
- TAG_PERSONALIZATION,
425
+ Tag.TAG_PERSONALIZATION,
363
426
  17,
364
427
  *self.vin.encode(),
365
- TAG_EPOCH,
428
+ Tag.TAG_EPOCH,
366
429
  len(hmac_personalized.epoch),
367
430
  *hmac_personalized.epoch,
368
- TAG_EXPIRES_AT,
431
+ Tag.TAG_EXPIRES_AT,
369
432
  4,
370
433
  *struct.pack(">I", hmac_personalized.expires_at),
371
- TAG_COUNTER,
434
+ Tag.TAG_COUNTER,
372
435
  4,
373
436
  *struct.pack(">I", hmac_personalized.counter),
374
- TAG_END,
437
+ Tag.TAG_END,
375
438
  ])
376
439
 
377
440
  hmac_personalized.tag = hmac.new(
@@ -400,27 +463,31 @@ class Commands(Vehicle):
400
463
  LOGGER.debug(f"Sending AES to domain {Domain.Name(session.domain)}")
401
464
 
402
465
  aes_personalized = session.aes_gcm_personalized()
466
+ flags = 1 << Flags.FLAG_ENCRYPT_RESPONSE
403
467
 
404
468
  metadata = bytes([
405
- TAG_SIGNATURE_TYPE,
469
+ Tag.TAG_SIGNATURE_TYPE,
406
470
  1,
407
- SIGNATURE_TYPE_AES_GCM_PERSONALIZED,
408
- TAG_DOMAIN,
471
+ SignatureType.SIGNATURE_TYPE_AES_GCM_PERSONALIZED,
472
+ Tag.TAG_DOMAIN,
409
473
  1,
410
474
  session.domain,
411
- TAG_PERSONALIZATION,
475
+ Tag.TAG_PERSONALIZATION,
412
476
  17,
413
477
  *self.vin.encode(),
414
- TAG_EPOCH,
478
+ Tag.TAG_EPOCH,
415
479
  len(aes_personalized.epoch),
416
480
  *aes_personalized.epoch,
417
- TAG_EXPIRES_AT,
481
+ Tag.TAG_EXPIRES_AT,
418
482
  4,
419
483
  *struct.pack(">I", aes_personalized.expires_at),
420
- TAG_COUNTER,
484
+ Tag.TAG_COUNTER,
421
485
  4,
422
486
  *struct.pack(">I", aes_personalized.counter),
423
- TAG_END,
487
+ Tag.TAG_FLAGS,
488
+ 4,
489
+ *struct.pack(">I", flags),
490
+ Tag.TAG_END,
424
491
  ])
425
492
 
426
493
  aad = Hash(SHA256())
@@ -431,7 +498,6 @@ class Commands(Vehicle):
431
498
 
432
499
  aes_personalized.tag = ct[-16:]
433
500
 
434
- # I think this whole section could be improved
435
501
  return RoutableMessage(
436
502
  to_destination=Destination(
437
503
  domain=session.domain,
@@ -446,7 +512,8 @@ class Commands(Vehicle):
446
512
  public_key=self._public_key
447
513
  ),
448
514
  AES_GCM_Personalized_data=aes_personalized,
449
- )
515
+ ),
516
+ flags=flags,
450
517
  )
451
518
 
452
519
 
@@ -454,10 +521,20 @@ class Commands(Vehicle):
454
521
  """Sign and send a message to Infotainment computer."""
455
522
  return await self._command(Domain.DOMAIN_VEHICLE_SECURITY, command.SerializeToString())
456
523
 
524
+ async def _getVehicleSecurity(self, command: UnsignedMessage) -> VehicleStatus:
525
+ """Sign and send a message to Infotainment computer."""
526
+ reply = await self._command(Domain.DOMAIN_VEHICLE_SECURITY, command.SerializeToString())
527
+ return reply["response"]
528
+
457
529
  async def _sendInfotainment(self, command: Action) -> dict[str, Any]:
458
530
  """Sign and send a message to Infotainment computer."""
459
531
  return await self._command(Domain.DOMAIN_INFOTAINMENT, command.SerializeToString())
460
532
 
533
+ async def _getInfotainment(self, command: Action) -> VehicleData:
534
+ """Sign and send a message to Infotainment computer."""
535
+ reply = await self._command(Domain.DOMAIN_INFOTAINMENT, command.SerializeToString())
536
+ return reply["response"]
537
+
461
538
  async def handshakeVehicleSecurity(self) -> None:
462
539
  """Perform a handshake with the vehicle security domain."""
463
540
  await self._handshake(Domain.DOMAIN_VEHICLE_SECURITY)
@@ -466,7 +543,7 @@ class Commands(Vehicle):
466
543
  """Perform a handshake with the infotainment domain."""
467
544
  await self._handshake(Domain.DOMAIN_INFOTAINMENT)
468
545
 
469
- async def _handshake(self, domain: Domain) -> None:
546
+ async def _handshake(self, domain: Domain) -> bool:
470
547
  """Perform a handshake with the vehicle."""
471
548
 
472
549
  LOGGER.debug(f"Handshake with domain {Domain.Name(domain)}")
@@ -483,7 +560,8 @@ class Commands(Vehicle):
483
560
  uuid=randbytes(16)
484
561
  )
485
562
 
486
- await self._send(msg)
563
+ await self._send(msg, "session_info")
564
+ return self._sessions[domain].ready
487
565
 
488
566
  async def ping(self) -> dict[str, Any]:
489
567
  """Ping the vehicle."""
@@ -4,7 +4,7 @@ from locale import getlocale
4
4
  from time import time
5
5
  from typing import TYPE_CHECKING, Any, List
6
6
 
7
- from ...const import (
7
+ from tesla_fleet_api.const import (
8
8
  CabinOverheatProtectionTemp,
9
9
  ClimateKeeperMode,
10
10
  Level,
@@ -15,12 +15,12 @@ from ...const import (
15
15
  VehicleDataEndpoint,
16
16
  WindowCommand,
17
17
  )
18
- from .vehicle import Vehicle
18
+ from tesla_fleet_api.tesla.vehicle.vehicle import Vehicle
19
19
 
20
20
  DEFAULT_LOCALE = (getlocale()[0] or "en-US").replace("_","-")
21
21
 
22
22
  if TYPE_CHECKING:
23
- from ..fleet import TeslaFleetApi
23
+ from tesla_fleet_api.tesla.fleet import TeslaFleetApi
24
24
 
25
25
  class VehicleFleet(Vehicle):
26
26
  """Class describing the Tesla Fleet API vehicle endpoints and commands."""
@@ -3,21 +3,22 @@ from __future__ import annotations
3
3
  import base64
4
4
  from typing import TYPE_CHECKING
5
5
 
6
- from .fleet import VehicleFleet
7
- from .commands import Commands
8
-
9
- from ...exceptions import (
6
+ from tesla_fleet_api.tesla.vehicle.fleet import VehicleFleet
7
+ from tesla_fleet_api.tesla.vehicle.commands import Commands
8
+ from tesla_fleet_api.exceptions import (
10
9
  MESSAGE_FAULTS,
10
+ NotOnWhitelistFault,
11
11
  )
12
- from .proto.signatures_pb2 import (
12
+ from tesla_fleet_api.tesla.vehicle.proto.signatures_pb2 import (
13
+ Session_Info_Status,
13
14
  SessionInfo,
14
15
  )
15
- from .proto.universal_message_pb2 import (
16
+ from tesla_fleet_api.tesla.vehicle.proto.universal_message_pb2 import (
16
17
  RoutableMessage,
17
18
  )
18
19
 
19
20
  if TYPE_CHECKING:
20
- from ..fleet import TeslaFleetApi
21
+ from tesla_fleet_api.tesla.fleet import TeslaFleetApi
21
22
 
22
23
 
23
24
  class VehicleSigned(VehicleFleet, Commands):
@@ -32,25 +33,17 @@ class VehicleSigned(VehicleFleet, Commands):
32
33
  super(Commands, self).__init__(parent, vin)
33
34
 
34
35
 
35
- async def _send(self, msg: RoutableMessage) -> RoutableMessage:
36
+ async def _send(self, msg: RoutableMessage, requires: str) -> RoutableMessage:
36
37
  """Serialize a message and send to the signed command endpoint."""
38
+ # requires isnt used because Fleet API messages are singular
37
39
 
38
40
  async with self._sessions[msg.to_destination.domain].lock:
39
- resp = await self.signed_command(
41
+ json = await self.signed_command(
40
42
  base64.b64encode(msg.SerializeToString()).decode()
41
43
  )
42
44
 
43
- resp_msg = RoutableMessage.FromString(base64.b64decode(resp["response"]))
44
-
45
- # Check UUID?
46
- # Check RoutingAdress?
47
-
48
- if resp_msg.session_info:
49
- self._sessions[resp_msg.from_destination.domain].update(
50
- SessionInfo.FromString(resp_msg.session_info), self.private_key
51
- )
45
+ resp = RoutableMessage.FromString(base64.b64decode(json["response"]))
52
46
 
53
- if resp_msg.signedMessageStatus.signed_message_fault:
54
- raise MESSAGE_FAULTS[resp_msg.signedMessageStatus.signed_message_fault]
47
+ self.validate_msg(resp)
55
48
 
56
- return resp_msg
49
+ return resp
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
3
3
 
4
4
 
5
5
  if TYPE_CHECKING:
6
- from ..tesla import Tesla
6
+ from tesla_fleet_api.tesla.tesla import Tesla
7
7
 
8
8
 
9
9
  class Vehicle:
@@ -1,13 +1,13 @@
1
1
  from __future__ import annotations
2
2
  from typing import TYPE_CHECKING
3
3
 
4
- from .signed import VehicleSigned
5
- from .bluetooth import VehicleBluetooth
6
- from .fleet import VehicleFleet
7
- from .vehicle import Vehicle
4
+ from tesla_fleet_api.tesla.vehicle.signed import VehicleSigned
5
+ from tesla_fleet_api.tesla.vehicle.bluetooth import VehicleBluetooth
6
+ from tesla_fleet_api.tesla.vehicle.fleet import VehicleFleet
7
+ from tesla_fleet_api.tesla.vehicle.vehicle import Vehicle
8
8
 
9
9
  if TYPE_CHECKING:
10
- from ..fleet import TeslaFleetApi
10
+ from tesla_fleet_api.tesla.fleet import TeslaFleetApi
11
11
 
12
12
 
13
13
 
@@ -31,7 +31,7 @@ class Vehicles(dict[str, Vehicle]):
31
31
  self[vin] = vehicle
32
32
  return vehicle
33
33
 
34
- def createBluetooth(self, vin: str) -> VehicleBluetooth:
34
+ def createBluetooth(self, vin: str):
35
35
  """Creates a bluetooth vehicle that uses command protocol."""
36
36
  vehicle = VehicleBluetooth(self._parent, vin)
37
37
  self[vin] = vehicle
@@ -1,4 +1,4 @@
1
- from .teslemetry import Teslemetry
1
+ from tesla_fleet_api.teslemetry.teslemetry import Teslemetry
2
2
 
3
3
  __all__ = [
4
4
  "Teslemetry",
@@ -2,12 +2,12 @@ from typing import Any
2
2
 
3
3
  import aiohttp
4
4
 
5
- from ..tesla.charging import Charging
6
- from ..tesla.energysite import EnergySites
7
- from ..tesla.user import User
8
- from .vehicle import TeslemetryVehicles
9
- from ..const import LOGGER, Method
10
- from ..tesla import TeslaFleetApi
5
+ from tesla_fleet_api.tesla.charging import Charging
6
+ from tesla_fleet_api.tesla.energysite import EnergySites
7
+ from tesla_fleet_api.tesla.user import User
8
+ from tesla_fleet_api.teslemetry.vehicle import TeslemetryVehicles
9
+ from tesla_fleet_api.const import LOGGER, Method
10
+ from tesla_fleet_api.tesla import TeslaFleetApi
11
11
 
12
12
  class Teslemetry(TeslaFleetApi):
13
13
 
@@ -1,15 +1,13 @@
1
1
  from __future__ import annotations
2
2
  from typing import TYPE_CHECKING, Any
3
3
 
4
- from ..const import Method
5
- from ..tesla.vehicle.proto.universal_message_pb2 import Domain
6
- from ..tesla.vehicle.vehicle import Vehicle
7
- from ..tesla.vehicle.vehicles import Vehicles
8
- from ..tesla.vehicle.bluetooth import VehicleBluetooth
9
- from ..tesla.vehicle.fleet import VehicleFleet
4
+ from tesla_fleet_api.const import Method
5
+ from tesla_fleet_api.tesla.vehicle.vehicle import Vehicle
6
+ from tesla_fleet_api.tesla.vehicle.vehicles import Vehicles
7
+ from tesla_fleet_api.tesla.vehicle.fleet import VehicleFleet
10
8
 
11
9
  if TYPE_CHECKING:
12
- from .teslemetry import Teslemetry
10
+ pass
13
11
 
14
12
  class TeslemetryVehicle(Vehicle):
15
13
  """Teslemetry specific base vehicle."""
@@ -1,4 +1,4 @@
1
- from .tessie import Tessie
1
+ from tesla_fleet_api.tessie.tessie import Tessie
2
2
 
3
3
  __all__ = [
4
4
  "Tessie",
@@ -1,12 +1,12 @@
1
1
  import aiohttp
2
2
  from typing import Any
3
3
 
4
- from ..tesla.charging import Charging
5
- from ..tesla.energysite import EnergySites
6
- from ..tesla.user import User
7
- from ..tesla import TeslaFleetApi
8
- from ..const import Method
9
- from .vehicle import TessieVehicles
4
+ from tesla_fleet_api.tesla.charging import Charging
5
+ from tesla_fleet_api.tesla.energysite import EnergySites
6
+ from tesla_fleet_api.tesla.user import User
7
+ from tesla_fleet_api.tesla import TeslaFleetApi
8
+ from tesla_fleet_api.const import Method
9
+ from tesla_fleet_api.tessie.vehicle import TessieVehicles
10
10
 
11
11
  class Tessie(TeslaFleetApi):
12
12
 
@@ -1,15 +1,9 @@
1
1
  from __future__ import annotations
2
- from typing import TYPE_CHECKING, Any
3
2
 
4
- from ..const import Method
5
- from ..tesla.vehicle.proto.universal_message_pb2 import Domain
6
- from ..tesla.vehicle.vehicle import Vehicle
7
- from ..tesla.vehicle.vehicles import Vehicles
8
- from ..tesla.vehicle.bluetooth import VehicleBluetooth
9
- from ..tesla.vehicle.fleet import VehicleFleet
3
+ from tesla_fleet_api.tesla.vehicle.vehicle import Vehicle
4
+ from tesla_fleet_api.tesla.vehicle.vehicles import Vehicles
5
+ from tesla_fleet_api.tesla.vehicle.fleet import VehicleFleet
10
6
 
11
- if TYPE_CHECKING:
12
- from .tessie import Tessie
13
7
 
14
8
  class TessieVehicle(Vehicle):
15
9
  """Tessie specific base vehicle."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tesla_fleet_api
3
- Version: 1.0.1
3
+ Version: 1.0.3
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