11from __future__ import annotations
22
33import hashlib
4- from asyncio import Future , get_running_loop
4+ import asyncio
55from typing import TYPE_CHECKING
66from google .protobuf .message import DecodeError
77
@@ -85,7 +85,7 @@ class VehicleBluetooth(Commands):
8585
8686 ble_name : str
8787 client : BleakClient
88- _futures : dict [Domain , Future ]
88+ _queues : dict [Domain , asyncio . Queue ]
8989 _ekey : ec .EllipticCurvePublicKey
9090 _recv : bytearray = bytearray ()
9191 _recv_len : int = 0
@@ -96,7 +96,10 @@ def __init__(
9696 ):
9797 super ().__init__ (parent , vin , key )
9898 self .ble_name = "S" + hashlib .sha1 (vin .encode ('utf-8' )).hexdigest ()[:16 ] + "C"
99- self ._futures = {}
99+ self ._queues = {
100+ Domain .DOMAIN_VEHICLE_SECURITY : asyncio .Queue (),
101+ Domain .DOMAIN_INFOTAINMENT : asyncio .Queue (),
102+ }
100103 if device is not None :
101104 self .client = BleakClient (device , services = [SERVICE_UUID ])
102105
@@ -135,7 +138,7 @@ async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
135138 """Exit the async context."""
136139 await self .disconnect ()
137140
138- def _on_notify (self ,sender : BleakGATTCharacteristic ,data : bytearray ) -> None :
141+ async def _on_notify (self ,sender : BleakGATTCharacteristic ,data : bytearray ) -> None :
139142 """Receive data from the Tesla BLE device."""
140143 if self ._recv_len :
141144 self ._recv += data
@@ -145,16 +148,16 @@ def _on_notify(self,sender: BleakGATTCharacteristic,data : bytearray) -> None:
145148 LOGGER .debug (f"Received { len (self ._recv )} of { self ._recv_len } bytes" )
146149 while len (self ._recv ) > self ._recv_len :
147150 LOGGER .warn (f"Received more data than expected: { len (self ._recv )} > { self ._recv_len } " )
148- self ._on_message (bytes (self ._recv [:self ._recv_len ]))
151+ await self ._on_message (bytes (self ._recv [:self ._recv_len ]))
149152 self ._recv_len = int .from_bytes (self ._recv [self ._recv_len :self ._recv_len + 2 ], 'big' )
150153 self ._recv = self ._recv [self ._recv_len + 2 :]
151154 continue
152155 if len (self ._recv ) == self ._recv_len :
153- self ._on_message (bytes (self ._recv ))
156+ await self ._on_message (bytes (self ._recv ))
154157 self ._recv = bytearray ()
155158 self ._recv_len = 0
156159
157- def _on_message (self , data :bytes ) -> None :
160+ async def _on_message (self , data :bytes ) -> None :
158161 """Receive messages from the Tesla BLE data."""
159162 try :
160163 msg = RoutableMessage .FromString (data )
@@ -164,57 +167,36 @@ def _on_message(self, data:bytes) -> None:
164167 self ._recv_len = 0
165168 return
166169
167- # Update Session
168- if (msg .session_info ):
169- info = SessionInfo .FromString (msg .session_info )
170- # maybe dont?
171- if (info .status == Session_Info_Status .SESSION_INFO_STATUS_KEY_NOT_ON_WHITELIST ):
172- self ._futures [msg .from_destination .domain ].set_exception (NotOnWhitelistFault ())
173- return
174- self ._sessions [msg .from_destination .domain ].update (info )
175-
176170 if (msg .to_destination .routing_address != self ._from_destination ):
177- # Get the ephemeral key here and save to self._ekey
178- return
179-
180- if (msg .from_destination .domain in self ._futures ):
181- LOGGER .debug (f"Received response for request { msg .request_uuid } " )
182- self ._futures [msg .from_destination .domain ].set_result (msg )
183- del self ._futures [msg .from_destination .domain ]
171+ # Ignore ephemeral key broadcasts
184172 return
185173
186- if msg .from_destination .domain == Domain .DOMAIN_VEHICLE_SECURITY :
187- submsg = FromVCSECMessage .FromString (msg .protobuf_message_as_bytes )
188- LOGGER .warning (f"Received orphaned VCSEC response: { submsg } " )
189- elif msg .from_destination .domain == Domain .DOMAIN_INFOTAINMENT :
190- submsg = Response .FromString (msg .protobuf_message_as_bytes )
191- LOGGER .warning (f"Received orphaned INFOTAINMENT response: { submsg } " )
192- else :
193- LOGGER .warning (f"Received orphaned response: { msg } " )
194-
195- async def _create_future (self , domain : Domain ) -> Future :
196- if (not self ._sessions [domain ].lock .locked ):
197- raise ValueError ("Session is not locked" )
198- self ._futures [domain ] = get_running_loop ().create_future ()
199- return self ._futures [domain ]
174+ LOGGER .info (f"Received response: { msg } " )
175+ await self ._queues [msg .from_destination .domain ].put (msg )
200176
201- async def _send (self , msg : RoutableMessage ) -> RoutableMessage :
177+ async def _send (self , msg : RoutableMessage , requires : str ) -> RoutableMessage :
202178 """Serialize a message and send to the vehicle and wait for a response."""
203179 domain = msg .to_destination .domain
204180 async with self ._sessions [domain ].lock :
205181 LOGGER .debug (f"Sending message { msg } " )
206- future = await self . _create_future ( domain )
182+
207183 payload = prependLength (msg .SerializeToString ())
208184
185+ # Empty the queue before sending the message
186+ while not self ._queues [domain ].empty ():
187+ await self ._queues [domain ].get ()
209188 await self .client .write_gatt_char (WRITE_UUID , payload , True )
210189
211- resp = await future
212- LOGGER .debug (f"Received message { resp } " )
190+ # Process the response
191+ async with asyncio .timeout (10 ):
192+ while True :
193+ resp = await self ._queues [domain ].get ()
194+ LOGGER .debug (f"Received message { resp } " )
213195
214- if resp .signedMessageStatus .signed_message_fault > 0 :
215- raise MESSAGE_FAULTS [resp .signedMessageStatus .signed_message_fault ]
196+ self .validate_msg (resp )
216197
217- return resp
198+ if resp .HasField (requires ):
199+ return resp
218200
219201 async def pair (self , role : Role = Role .ROLE_OWNER , form : KeyFormFactor = KeyFormFactor .KEY_FORM_FACTOR_CLOUD_KEY ):
220202 """Pair the key."""
0 commit comments