tesla-fleet-api 1.0.7__py3-none-any.whl → 1.0.9__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.7"
6
+ VERSION = "1.0.9"
7
7
  LOGGER = logging.getLogger(__package__)
8
8
  SERVERS = {
9
9
  "na": "https://fleet-api.prd.na.vn.cloud.tesla.com",
@@ -875,9 +875,6 @@ SIGNED_MESSAGE_INFORMATION_FAULTS = [
875
875
  class WhitelistOperationStatus(TeslaFleetError):
876
876
  message = "Whitelist operation failed"
877
877
 
878
- def __init__(self, message):
879
- self.message = message
880
-
881
878
  class WhitelistOperationUndocumentedError(WhitelistOperationStatus):
882
879
  message = "Undocumented whitelist operation error"
883
880
  code = 1
@@ -3,21 +3,21 @@
3
3
  import hashlib
4
4
  import re
5
5
  from google.protobuf.json_format import MessageToJson, MessageToDict
6
- from bleak.backends.device import BLEDevice
7
- from cryptography.hazmat.primitives.asymmetric import ec
8
6
 
9
7
  from tesla_fleet_api.tesla.tesla import Tesla
10
- from tesla_fleet_api.tesla.vehicle.bluetooth import VehicleBluetooth
8
+ from tesla_fleet_api.tesla.vehicle.vehicles import VehiclesBluetooth
11
9
 
12
10
  class TeslaBluetooth(Tesla):
13
11
  """Class describing a Tesla Bluetooth connection."""
14
12
 
13
+ Vehicles = VehiclesBluetooth
14
+
15
15
  def __init__(
16
16
  self,
17
17
  ):
18
18
  """Initialize the Tesla Fleet API."""
19
19
 
20
- self.vehicles = Vehicles(self)
20
+ self.vehicles = self.Vehicles(self)
21
21
 
22
22
  def valid_name(self, name: str) -> bool:
23
23
  """Check if a BLE device name is a valid Tesla vehicle."""
@@ -27,23 +27,8 @@ class TeslaBluetooth(Tesla):
27
27
  """Get the name of a vehicle."""
28
28
  return "S" + hashlib.sha1(vin.encode('utf-8')).hexdigest()[:16] + "C"
29
29
 
30
- class Vehicles(dict[str, VehicleBluetooth]):
31
- """Class containing and creating vehicles."""
32
-
33
- _parent: TeslaBluetooth
34
-
35
- def __init__(self, parent: TeslaBluetooth):
36
- self._parent = parent
37
-
38
- def create(self, vin: str) -> VehicleBluetooth:
39
- """Creates a specific vehicle."""
40
- return self.createBluetooth(vin)
41
30
 
42
- def createBluetooth(self, vin: str, key: ec.EllipticCurvePrivateKey | None = None, device: None | str | BLEDevice = None) -> VehicleBluetooth:
43
- """Creates a specific vehicle."""
44
- vehicle = VehicleBluetooth(self._parent, vin, key, device)
45
- self[vin] = vehicle
46
- return vehicle
31
+ # Helpers
47
32
 
48
33
  def toJson(message) -> str:
49
34
  """Convert a protobuf message to JSON."""
@@ -7,11 +7,6 @@ import aiohttp
7
7
  from tesla_fleet_api.tesla.tesla import Tesla
8
8
  from tesla_fleet_api.exceptions import raise_for_status, InvalidRegion, LibraryError, ResponseError
9
9
  from tesla_fleet_api.const import SERVERS, Method, LOGGER, VERSION
10
- from tesla_fleet_api.tesla.charging import Charging
11
- from tesla_fleet_api.tesla.energysite import EnergySites
12
- from tesla_fleet_api.tesla.partner import Partner
13
- from tesla_fleet_api.tesla.user import User
14
- from tesla_fleet_api.tesla.vehicle.vehicles import Vehicles
15
10
 
16
11
 
17
12
  # Based on https://developer.tesla.com/docs/fleet-api
@@ -54,15 +49,15 @@ class TeslaFleetApi(Tesla):
54
49
  LOGGER.debug("Using server %s", self.server)
55
50
 
56
51
  if charging_scope:
57
- self.charging = Charging(self)
52
+ self.charging = self.Charging(self)
58
53
  if energy_scope:
59
- self.energySites = EnergySites(self)
54
+ self.energySites = self.EnergySites(self)
60
55
  if user_scope:
61
- self.user = User(self)
56
+ self.user = self.User(self)
62
57
  if partner_scope:
63
- self.partner = Partner(self)
58
+ self.partner = self.Partner(self)
64
59
  if vehicle_scope:
65
- self.vehicles = Vehicles(self)
60
+ self.vehicles = self.Vehicles(self)
66
61
 
67
62
  async def find_server(self) -> str:
68
63
  """Find the server URL for the Tesla Fleet API."""
@@ -3,6 +3,12 @@
3
3
  from os.path import exists
4
4
  import aiofiles
5
5
 
6
+ from tesla_fleet_api.tesla.charging import Charging
7
+ from tesla_fleet_api.tesla.energysite import EnergySites
8
+ from tesla_fleet_api.tesla.partner import Partner
9
+ from tesla_fleet_api.tesla.user import User
10
+ from tesla_fleet_api.tesla.vehicle.vehicles import Vehicles
11
+
6
12
  # cryptography
7
13
  from cryptography.hazmat.primitives.asymmetric import ec
8
14
  from cryptography.hazmat.primitives import serialization
@@ -11,6 +17,12 @@ from cryptography.hazmat.backends import default_backend
11
17
  class Tesla:
12
18
  """Base class describing interactions with Tesla products."""
13
19
 
20
+ Charging = Charging
21
+ EnergySites = EnergySites
22
+ Partner = Partner
23
+ User = User
24
+ Vehicles = Vehicles
25
+
14
26
  private_key: ec.EllipticCurvePrivateKey | None = None
15
27
 
16
28
  async def get_private_key(
@@ -86,7 +86,7 @@ class VehicleBluetooth(Commands):
86
86
  _auth_method = "aes"
87
87
 
88
88
  def __init__(
89
- self, parent: Tesla, vin: str, key: ec.EllipticCurvePrivateKey | None = None, device: None | str | BLEDevice = None
89
+ self, parent: Tesla, vin: str, key: ec.EllipticCurvePrivateKey | None = None, device: BLEDevice | None = None
90
90
  ):
91
91
  super().__init__(parent, vin, key)
92
92
  self.ble_name = "S" + hashlib.sha1(vin.encode('utf-8')).hexdigest()[:16] + "C"
@@ -95,14 +95,18 @@ class VehicleBluetooth(Commands):
95
95
  Domain.DOMAIN_INFOTAINMENT: asyncio.Queue(),
96
96
  }
97
97
  if device is not None:
98
- self.client = BleakClient(device, services=[SERVICE_UUID])
98
+ self.device = device
99
99
 
100
- async def find_vehicle(self, name: str | None = None, address: str | None = None, scanner: BleakScanner = BleakScanner()) -> BLEDevice:
100
+ async def find_vehicle(self, name: str | None = None, address: str | None = None, scanner: BleakScanner | None = None) -> BLEDevice:
101
101
  """Find the Tesla BLE device."""
102
- if name is not None:
103
- device = await scanner.find_device_by_name(name)
104
- elif address is not None:
102
+
103
+ if scanner is None:
104
+ scanner = BleakScanner(service_uuids=[SERVICE_UUID])
105
+
106
+ if address is not None:
105
107
  device = await scanner.find_device_by_address(address)
108
+ elif name is not None:
109
+ device = await scanner.find_device_by_name(name)
106
110
  else:
107
111
  device = await scanner.find_device_by_name(self.ble_name)
108
112
  if not device:
@@ -116,18 +120,17 @@ class VehicleBluetooth(Commands):
116
120
  def get_device(self) -> BLEDevice:
117
121
  return self.device
118
122
 
119
- async def connect(self, device: BLEDevice | None = None, max_attempts: int = MAX_CONNECT_ATTEMPTS) -> None:
123
+ async def connect(self, max_attempts: int = MAX_CONNECT_ATTEMPTS) -> None:
120
124
  """Connect to the Tesla BLE device."""
121
- if device:
122
- self.device = device
123
- if not self.device:
124
- raise ValueError(f"Device {self.ble_name} not found")
125
+ if not hasattr(self, 'device'):
126
+ raise ValueError(f"BLEDevice {self.ble_name} has not been found or set")
125
127
  self.client = await establish_connection(
126
128
  BleakClient,
127
129
  self.device,
128
130
  self.vin,
129
131
  max_attempts=max_attempts,
130
- ble_device_callback=self.get_device
132
+ #ble_device_callback=self.get_device,
133
+ services=[SERVICE_UUID]
131
134
  )
132
135
  await self.client.start_notify(READ_UUID, self._on_notify)
133
136
 
@@ -189,7 +192,7 @@ class VehicleBluetooth(Commands):
189
192
  LOGGER.debug(f"Received response: {msg}")
190
193
  await self._queues[msg.from_destination.domain].put(msg)
191
194
 
192
- async def _send(self, msg: RoutableMessage, requires: str) -> RoutableMessage:
195
+ async def _send(self, msg: RoutableMessage, requires: str, timeout: int = 2) -> RoutableMessage:
193
196
  """Serialize a message and send to the vehicle and wait for a response."""
194
197
  domain = msg.to_destination.domain
195
198
  async with self._sessions[domain].lock:
@@ -203,7 +206,7 @@ class VehicleBluetooth(Commands):
203
206
  await self.client.write_gatt_char(WRITE_UUID, payload, True)
204
207
 
205
208
  # Process the response
206
- async with asyncio.timeout(2):
209
+ async with asyncio.timeout(timeout):
207
210
  while True:
208
211
  resp = await self._queues[domain].get()
209
212
  LOGGER.debug(f"Received message {resp}")
@@ -213,7 +216,8 @@ class VehicleBluetooth(Commands):
213
216
  if resp.HasField(requires):
214
217
  return resp
215
218
 
216
- async def pair(self, role: Role = Role.ROLE_OWNER, form: KeyFormFactor = KeyFormFactor.KEY_FORM_FACTOR_CLOUD_KEY):
219
+
220
+ async def pair(self, role: Role = Role.ROLE_OWNER, form: KeyFormFactor = KeyFormFactor.KEY_FORM_FACTOR_CLOUD_KEY, timeout: int = 60):
217
221
  """Pair the key."""
218
222
 
219
223
  request = UnsignedMessage(
@@ -236,7 +240,7 @@ class VehicleBluetooth(Commands):
236
240
  ),
237
241
  protobuf_message_as_bytes=request.SerializeToString(),
238
242
  )
239
- resp = await self._send(msg, "protobuf_message_as_bytes")
243
+ resp = await self._send(msg, "protobuf_message_as_bytes", timeout)
240
244
  respMsg = FromVCSECMessage.FromString(resp.protobuf_message_as_bytes)
241
245
  if(respMsg.commandStatus.whitelistOperationStatus.whitelistOperationInformation):
242
246
  if(respMsg.commandStatus.whitelistOperationStatus.whitelistOperationInformation < len(WHITELIST_OPERATION_STATUS)):
@@ -183,7 +183,7 @@ class Session:
183
183
  self.counter = sessionInfo.counter
184
184
  self.epoch = sessionInfo.epoch
185
185
  self.delta = int(time.time()) - sessionInfo.clock_time
186
- if (not self.ready or self.publicKey != sessionInfo.publicKey):
186
+ if (not self.ready or getattr(self, "publicKey", "None") != sessionInfo.publicKey):
187
187
  self.publicKey = sessionInfo.publicKey
188
188
  self.sharedKey = self.parent.shared_key(sessionInfo.publicKey)
189
189
  self.hmac = hmac.new(self.sharedKey, "authenticated command".encode(), hashlib.sha256).digest()
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
  from typing import TYPE_CHECKING
3
+ from bleak.backends.device import BLEDevice
4
+ from cryptography.hazmat.primitives.asymmetric import ec
3
5
 
4
6
  from tesla_fleet_api.tesla.vehicle.signed import VehicleSigned
5
7
  from tesla_fleet_api.tesla.vehicle.bluetooth import VehicleBluetooth
@@ -8,7 +10,7 @@ from tesla_fleet_api.tesla.vehicle.vehicle import Vehicle
8
10
 
9
11
  if TYPE_CHECKING:
10
12
  from tesla_fleet_api.tesla.fleet import TeslaFleetApi
11
-
13
+ from tesla_fleet_api.tesla.bluetooth import TeslaBluetooth
12
14
 
13
15
 
14
16
  class Vehicles(dict[str, Vehicle]):
@@ -47,3 +49,23 @@ class Vehicles(dict[str, Vehicle]):
47
49
  def specificSigned(self, vin: str) -> VehicleSigned:
48
50
  """Legacy method for creating a Fleet API vehicle that uses command protocol."""
49
51
  return self.createSigned(vin)
52
+
53
+
54
+ class VehiclesBluetooth(dict[str, Vehicle]):
55
+ """Class containing and creating bluetooth vehicles."""
56
+
57
+ _parent: TeslaBluetooth
58
+ Bluetooth = VehicleBluetooth
59
+
60
+ def __init__(self, parent: TeslaBluetooth):
61
+ self._parent = parent
62
+
63
+ def create(self, vin: str, key: ec.EllipticCurvePrivateKey | None = None, device: BLEDevice | None = None) -> VehicleBluetooth:
64
+ """Creates a bluetooth vehicle that uses command protocol."""
65
+ return self.createBluetooth(vin, key, device)
66
+
67
+ def createBluetooth(self, vin: str, key: ec.EllipticCurvePrivateKey | None = None, device: BLEDevice | None = None) -> VehicleBluetooth:
68
+ """Creates a bluetooth vehicle that uses command protocol."""
69
+ vehicle = self.Bluetooth(self._parent, vin, key, device)
70
+ self[vin] = vehicle
71
+ return vehicle
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tesla_fleet_api
3
- Version: 1.0.7
3
+ Version: 1.0.9
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
@@ -1,23 +1,23 @@
1
1
  tesla_fleet_api/__init__.py,sha256=3DZMoZ-5srW-7SooAjqcRubQDuZPY8rMKH7eqIp4qtg,392
2
- tesla_fleet_api/const.py,sha256=SBoj3pXbqLxQ0upG6_D3oV-um6M6OGSLXoN6eLQqajs,3748
3
- tesla_fleet_api/exceptions.py,sha256=iZJv_6YJO6wK_lYvR18SugpNk-ZTzElmcsOqNqd05M0,35495
2
+ tesla_fleet_api/const.py,sha256=6avk1nS7GL_OVZaiC1Cmy0a-QoZ3LNoXTUjxFNKiPWA,3748
3
+ tesla_fleet_api/exceptions.py,sha256=GvjJBR77xGt2g3vhiAxcNtysj-e1Me7F-9PwO9dyzOo,35430
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=WhDo66OEwCGhvsGskvWcGUiNv6eyFsdEiQdIm571jQM,1835
6
+ tesla_fleet_api/tesla/bluetooth.py,sha256=vxDPTM-Uq9IAVJjK54-p_llj26hssEEXAQI5-lVGx8Q,1137
7
7
  tesla_fleet_api/tesla/charging.py,sha256=D7I7cAf-3-95sIjyP6wpVqCq9Cppj6U-VPFQGpQQ8bs,1704
8
8
  tesla_fleet_api/tesla/energysite.py,sha256=vStffklBQfQNAO_1wrHLFu7BlBCTVVbLh7_IrAUL3wg,6131
9
- tesla_fleet_api/tesla/fleet.py,sha256=X74tzwGO9w65j9YUGuW04CwG7Bx6biEHwxIjWGCzB4c,5670
9
+ tesla_fleet_api/tesla/fleet.py,sha256=zfmagXF4TbbVOPWcngCSKebaGB1daXbw8mTJ9o8einY,5432
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
- tesla_fleet_api/tesla/tesla.py,sha256=Jlz90-fM0nJbhnQN0k3ukNv59-9KqZZbyQ91IiLIbfo,2010
12
+ tesla_fleet_api/tesla/tesla.py,sha256=Gs8-L3OsEMhs1N_vdDx-E46bOHKGowXTBxmRiP8NKh4,2391
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=25UlaAatKxu_NzbG5te5sKHeM9jAufj42LjGJaIKNHg,16476
16
- tesla_fleet_api/tesla/vehicle/commands.py,sha256=zgfc2yo1UxEh8ePqSb-4h0UTK0RmpCG_9LZX44CES2c,51268
15
+ tesla_fleet_api/tesla/vehicle/bluetooth.py,sha256=SAzJL0GW8Gp5cCs_Gyc2qBJP9WVVwV_qwUFkQF8waTI,16561
16
+ tesla_fleet_api/tesla/vehicle/commands.py,sha256=_LXAtuAIkeojJuw8k1QjEabAQQH2MLD5YZyUH58QoSg,51288
17
17
  tesla_fleet_api/tesla/vehicle/fleet.py,sha256=K9BVZj6CChJSDSMFroa7Cz0KrsYWj32ILtQumarkLaU,32080
18
18
  tesla_fleet_api/tesla/vehicle/signed.py,sha256=RUzVnZIfykz3YZW2gaxd1iaN1i8LkLaEoiXrbqZn9kg,1339
19
19
  tesla_fleet_api/tesla/vehicle/vehicle.py,sha256=BlmHjV5Hly8v0vWNVBUhdNvwsSSuCuF946D7zROUB3s,823
20
- tesla_fleet_api/tesla/vehicle/vehicles.py,sha256=axiH33YRnkYcbQmolbL85PF69SRI_5FA5T10v-cMmBc,1644
20
+ tesla_fleet_api/tesla/vehicle/vehicles.py,sha256=c1KFTAYDvuTVNjM5O23KvT54UbNwo3ofFVpfkY-Mq5Y,2640
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/vehicles.py,sha256=9nybVg7VHKLa2woMG6fzMmQP6xJIE_jdAd
44
44
  tesla_fleet_api/tessie/__init__.py,sha256=9lhQJaB6X4PObUL9QdaaZYqs2BxiTidu3zmHcBESLVw,78
45
45
  tesla_fleet_api/tessie/tessie.py,sha256=uhg0oOIxpwDvlvdBhKHeF3AGR2PzmdBgzh2-_EkmSq0,2617
46
46
  tesla_fleet_api/tessie/vehicles.py,sha256=gfEatilI_ct-R4CM5xYhrlduqCR9IHlyc56WmJf7v7k,1149
47
- tesla_fleet_api-1.0.7.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
48
- tesla_fleet_api-1.0.7.dist-info/METADATA,sha256=8V2QJ6JsJYg745xq--pFzS1n8tm30avdLd-bEfmatkk,4382
49
- tesla_fleet_api-1.0.7.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
50
- tesla_fleet_api-1.0.7.dist-info/top_level.txt,sha256=jeNbog_1saXBFrGpom9WyPWmilxsyP3szL_G7JLWQfM,16
51
- tesla_fleet_api-1.0.7.dist-info/RECORD,,
47
+ tesla_fleet_api-1.0.9.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
48
+ tesla_fleet_api-1.0.9.dist-info/METADATA,sha256=bDVsevE52Rb5wKU46C2oUVlDHMmMp9zClJl2KQlRpjk,4382
49
+ tesla_fleet_api-1.0.9.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
50
+ tesla_fleet_api-1.0.9.dist-info/top_level.txt,sha256=jeNbog_1saXBFrGpom9WyPWmilxsyP3szL_G7JLWQfM,16
51
+ tesla_fleet_api-1.0.9.dist-info/RECORD,,