Skip to content

Commit e3ceb49

Browse files
committed
Fixes
1 parent faae869 commit e3ceb49

File tree

7 files changed

+71
-43
lines changed

7 files changed

+71
-43
lines changed

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
[tool.ruff]
22
exclude = ["tesla_fleet_api/tesla/vehicle/proto/*"]
3+
4+
[tool.pyright]
5+
exclude = ["tesla_fleet_api/tesla/vehicle/proto/*"]

tesla_fleet_api/exceptions.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ class ResponseError(TeslaFleetError):
2121
"""The response from the server was not JSON."""
2222

2323
message = "The response from the server was not JSON."
24-
data: str | None = None
2524

2625

2726
class InvalidCommand(TeslaFleetError):

tesla_fleet_api/tesla/energysite.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,12 @@ class EnergySites(dict[int, EnergySite]):
159159
"""Class describing the Tesla Fleet API partner endpoints"""
160160

161161
_parent: TeslaFleetApi
162+
Site = EnergySite
162163

163164
def __init__(self, parent: TeslaFleetApi):
164165
self._parent = parent
165166

166167
def create(self, energy_site_id: int) -> EnergySite:
167168
"""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)
169170
return self[energy_site_id]

tesla_fleet_api/tesla/vehicle/bluetooth.py

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,17 @@ def get_device(self) -> BLEDevice:
118118

119119
async def connect(self, device: BLEDevice | None = None, max_attempts: int = MAX_CONNECT_ATTEMPTS) -> None:
120120
"""Connect to the Tesla BLE device."""
121-
self.client = await establish_connection(BleakClient, self.device, self.vin, max_attempts=max_attempts, ble_device_callback=self.get_device)
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+
)
122132
await self.client.start_notify(READ_UUID, self._on_notify)
123133

124134
async def disconnect(self) -> bool:
@@ -140,34 +150,43 @@ async def _on_notify(self,sender: BleakGATTCharacteristic,data : bytearray) -> N
140150
self._recv += data
141151
else:
142152
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
143158
self._recv = data[2:]
144-
LOGGER.debug(f"Received {len(self._recv)} of {self._recv_len} bytes")
145-
while len(self._recv) > self._recv_len:
146-
LOGGER.warn(f"Received more data than expected: {len(self._recv)} > {self._recv_len}")
147-
await self._on_message(bytes(self._recv[:self._recv_len]))
148-
self._recv_len = int.from_bytes(self._recv[self._recv_len:self._recv_len+2], 'big')
149-
self._recv = self._recv[self._recv_len+2:]
150-
continue
151-
if len(self._recv) == self._recv_len:
152-
await self._on_message(bytes(self._recv))
153-
self._recv = bytearray()
154-
self._recv_len = 0
155-
156-
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:
157183
"""Receive messages from the Tesla BLE data."""
158-
try:
159-
msg = RoutableMessage.FromString(data)
160-
except DecodeError as e:
161-
LOGGER.error(f"Error parsing message: {e}")
162-
self._recv = bytearray()
163-
self._recv_len = 0
164-
return
165184

166185
if(msg.to_destination.routing_address != self._from_destination):
167186
# Ignore ephemeral key broadcasts
168187
return
169188

170-
LOGGER.info(f"Received response: {msg}")
189+
LOGGER.debug(f"Received response: {msg}")
171190
await self._queues[msg.from_destination.domain].put(msg)
172191

173192
async def _send(self, msg: RoutableMessage, requires: str) -> RoutableMessage:
@@ -184,7 +203,7 @@ async def _send(self, msg: RoutableMessage, requires: str) -> RoutableMessage:
184203
await self.client.write_gatt_char(WRITE_UUID, payload, True)
185204

186205
# Process the response
187-
async with asyncio.timeout(10):
206+
async with asyncio.timeout(2):
188207
while True:
189208
resp = await self._queues[domain].get()
190209
LOGGER.debug(f"Received message {resp}")
@@ -217,7 +236,7 @@ async def pair(self, role: Role = Role.ROLE_OWNER, form: KeyFormFactor = KeyForm
217236
),
218237
protobuf_message_as_bytes=request.SerializeToString(),
219238
)
220-
resp = await self._send(msg)
239+
resp = await self._send(msg, "protobuf_message_as_bytes")
221240
respMsg = FromVCSECMessage.FromString(resp.protobuf_message_as_bytes)
222241
if(respMsg.commandStatus.whitelistOperationStatus.whitelistOperationInformation):
223242
if(respMsg.commandStatus.whitelistOperationStatus.whitelistOperationInformation < len(WHITELIST_OPERATION_STATUS)):

tesla_fleet_api/tesla/vehicle/signed.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,6 @@
55

66
from tesla_fleet_api.tesla.vehicle.fleet import VehicleFleet
77
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-
)
168
from tesla_fleet_api.tesla.vehicle.proto.universal_message_pb2 import (
179
RoutableMessage,
1810
)
@@ -41,9 +33,6 @@ async def _send(self, msg: RoutableMessage, requires: str) -> RoutableMessage:
4133
json = await self.signed_command(
4234
base64.b64encode(msg.SerializeToString()).decode()
4335
)
44-
4536
resp = RoutableMessage.FromString(base64.b64decode(json["response"]))
46-
4737
self.validate_msg(resp)
48-
4938
return resp
Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
from __future__ import annotations
22
from typing import TYPE_CHECKING
33

4-
54
if TYPE_CHECKING:
65
from tesla_fleet_api.tesla.tesla import Tesla
76

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+
}
816

917
class Vehicle:
1018
"""Base class describing a Tesla vehicle."""
@@ -14,6 +22,12 @@ class Vehicle:
1422
def __init__(self, parent: Tesla, vin: str):
1523
self.vin = vin
1624

17-
def pre2021(self, vin: str) -> bool:
25+
26+
def pre2021(self) -> bool:
1827
"""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")

tesla_fleet_api/tesla/vehicle/vehicles.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,28 @@ class Vehicles(dict[str, Vehicle]):
1515
"""Class containing and creating vehicles."""
1616

1717
_parent: TeslaFleetApi
18+
Fleet = VehicleFleet
19+
Signed = VehicleSigned
20+
Bluetooth = VehicleBluetooth
1821

1922
def __init__(self, parent: TeslaFleetApi):
2023
self._parent = parent
2124

2225
def createFleet(self, vin: str) -> VehicleFleet:
2326
"""Creates a Fleet API vehicle."""
24-
vehicle = VehicleFleet(self._parent, vin)
27+
vehicle = self.Fleet(self._parent, vin)
2528
self[vin] = vehicle
2629
return vehicle
2730

2831
def createSigned(self, vin: str) -> VehicleSigned:
2932
"""Creates a Fleet API vehicle that uses command protocol."""
30-
vehicle = VehicleSigned(self._parent, vin)
33+
vehicle = self.Signed(self._parent, vin)
3134
self[vin] = vehicle
3235
return vehicle
3336

3437
def createBluetooth(self, vin: str):
3538
"""Creates a bluetooth vehicle that uses command protocol."""
36-
vehicle = VehicleBluetooth(self._parent, vin)
39+
vehicle = self.Bluetooth(self._parent, vin)
3740
self[vin] = vehicle
3841
return vehicle
3942

0 commit comments

Comments
 (0)