1616
1717from tesla_fleet_api .const import (
1818 LOGGER ,
19+ BluetoothVehicleData
1920)
2021from tesla_fleet_api .exceptions import (
2122 MESSAGE_FAULTS ,
2223 WHITELIST_OPERATION_STATUS ,
2324 WhitelistOperationStatus ,
25+ NotOnWhitelistFault ,
2426)
2527
2628# Protocol
2729from tesla_fleet_api .tesla .vehicle .proto .car_server_pb2 import (
30+ Action ,
31+ VehicleAction ,
2832 Response ,
33+ GetVehicleData ,
34+ GetChargeState ,
35+ GetClimateState ,
36+ GetDriveState ,
37+ GetLocationState ,
38+ GetClosuresState ,
39+ GetChargeScheduleState ,
40+ GetPreconditioningScheduleState ,
41+ GetTirePressureState ,
42+ GetMediaState ,
43+ GetMediaDetailState ,
44+ GetSoftwareUpdateState ,
45+ GetParentalControlsState ,
2946)
3047from tesla_fleet_api .tesla .vehicle .proto .signatures_pb2 import (
3148 SessionInfo ,
49+ Session_Info_Status
3250)
3351from tesla_fleet_api .tesla .vehicle .proto .universal_message_pb2 import (
3452 Destination ,
4462 RKEAction_E ,
4563 UnsignedMessage ,
4664 WhitelistOperation ,
47-
4865)
4966
5067SERVICE_UUID = "00000211-b2d1-43f0-9b88-960cebf8b91e"
@@ -64,40 +81,40 @@ class VehicleBluetooth(Commands):
6481
6582 ble_name : str
6683 client : BleakClient
67- _device : BLEDevice
6884 _futures : dict [Domain , Future ]
6985 _ekey : ec .EllipticCurvePublicKey
7086 _recv : bytearray = bytearray ()
7187 _recv_len : int = 0
7288 _auth_method = "aes"
7389
7490 def __init__ (
75- self , parent : Tesla , vin : str , key : ec .EllipticCurvePrivateKey | None = None
91+ self , parent : Tesla , vin : str , key : ec .EllipticCurvePrivateKey | None = None , device : None | str | BLEDevice = None
7692 ):
7793 super ().__init__ (parent , vin , key )
7894 self .ble_name = "S" + hashlib .sha1 (vin .encode ('utf-8' )).hexdigest ()[:16 ] + "C"
7995 self ._futures = {}
96+ if device is not None :
97+ self .client = BleakClient (device , services = [SERVICE_UUID ])
8098
8199 async def find_client (self , scanner : BleakScanner = BleakScanner ()) -> BleakClient :
82100 """Find the Tesla BLE device."""
83101
84102 device = await scanner .find_device_by_name (self .ble_name )
85103 if not device :
86104 raise ValueError (f"Device { self .ble_name } not found" )
87- self ._device = device
88- self .client = BleakClient (self ._device , services = [SERVICE_UUID ])
89- LOGGER .debug (f"Discovered device { self ._device .name } { self ._device .address } " )
105+ self .client = BleakClient (device , services = [SERVICE_UUID ])
106+ LOGGER .debug (f"Discovered device { device .name } { device .address } " )
90107 return self .client
91108
92- def create_client (self , mac : str ) -> BleakClient :
93- """Create a client using a MAC address."""
94- self .client = BleakClient (mac , services = [SERVICE_UUID ])
109+ def create_client (self , device : str | BLEDevice ) -> BleakClient :
110+ """Create a client using a MAC address or Bleak Device ."""
111+ self .client = BleakClient (device , services = [SERVICE_UUID ])
95112 return self .client
96113
97- async def connect (self , mac : str | None = None ) -> None :
114+ async def connect (self , device : str | BLEDevice | None = None ) -> None :
98115 """Connect to the Tesla BLE device."""
99- if mac is not None :
100- self .create_client (mac )
116+ if device is not None :
117+ self .create_client (device )
101118 await self .client .connect ()
102119 await self .client .start_notify (READ_UUID , self ._on_notify )
103120
@@ -139,12 +156,17 @@ def _on_message(self, data:bytes) -> None:
139156 msg = RoutableMessage .FromString (data )
140157 except DecodeError as e :
141158 LOGGER .error (f"Error parsing message: { e } " )
159+ self ._recv = bytearray ()
160+ self ._recv_len = 0
142161 return
143162
144163 # Update Session
145164 if (msg .session_info ):
146165 info = SessionInfo .FromString (msg .session_info )
147- LOGGER .debug (f"Received session info: { info } " )
166+ # maybe dont?
167+ if (info .status == Session_Info_Status .SESSION_INFO_STATUS_KEY_NOT_ON_WHITELIST ):
168+ self ._futures [msg .from_destination .domain ].set_exception (NotOnWhitelistFault ())
169+ return
148170 self ._sessions [msg .from_destination .domain ].update (info )
149171
150172 if (msg .to_destination .routing_address != self ._from_destination ):
@@ -162,6 +184,8 @@ def _on_message(self, data:bytes) -> None:
162184 elif msg .from_destination .domain == Domain .DOMAIN_INFOTAINMENT :
163185 submsg = Response .FromString (msg .protobuf_message_as_bytes )
164186 LOGGER .warning (f"Received orphaned INFOTAINMENT response: { submsg } " )
187+ else :
188+ LOGGER .warning (f"Received orphaned response: { msg } " )
165189
166190 async def _create_future (self , domain : Domain ) -> Future :
167191 if (not self ._sessions [domain ].lock .locked ):
@@ -182,7 +206,7 @@ async def _send(self, msg: RoutableMessage) -> RoutableMessage:
182206 resp = await future
183207 LOGGER .debug (f"Received message { resp } " )
184208
185- if resp .signedMessageStatus .signed_message_fault :
209+ if resp .signedMessageStatus .signed_message_fault > 0 :
186210 raise MESSAGE_FAULTS [resp .signedMessageStatus .signed_message_fault ]
187211
188212 return resp
@@ -224,3 +248,26 @@ async def wake_up(self):
224248 return await self ._sendVehicleSecurity (
225249 UnsignedMessage (RKEAction = RKEAction_E .RKE_ACTION_WAKE_VEHICLE )
226250 )
251+
252+ async def vehicle_data (self , endpoints : list [BluetoothVehicleData ]):
253+ """Get vehicle data."""
254+ return await self ._sendInfotainment (
255+ Action (
256+ vehicleAction = VehicleAction (
257+ getVehicleData = GetVehicleData (
258+ getChargeState = GetChargeState () if BluetoothVehicleData .CHARGE_STATE in endpoints else None ,
259+ getClimateState = GetClimateState () if BluetoothVehicleData .CLIMATE_STATE in endpoints else None ,
260+ getDriveState = GetDriveState () if BluetoothVehicleData .DRIVE_STATE in endpoints else None ,
261+ getLocationState = GetLocationState () if BluetoothVehicleData .LOCATION_STATE in endpoints else None ,
262+ getClosuresState = GetClosuresState () if BluetoothVehicleData .CLOSURES_STATE in endpoints else None ,
263+ getChargeScheduleState = GetChargeScheduleState () if BluetoothVehicleData .CHARGE_SCHEDULE_STATE in endpoints else None ,
264+ getPreconditioningScheduleState = GetPreconditioningScheduleState () if BluetoothVehicleData .PRECONDITIONING_SCHEDULE_STATE in endpoints else None ,
265+ getTirePressureState = GetTirePressureState () if BluetoothVehicleData .TIRE_PRESSURE_STATE in endpoints else None ,
266+ getMediaState = GetMediaState () if BluetoothVehicleData .MEDIA_STATE in endpoints else None ,
267+ getMediaDetailState = GetMediaDetailState () if BluetoothVehicleData .MEDIA_DETAIL_STATE in endpoints else None ,
268+ getSoftwareUpdateState = GetSoftwareUpdateState () if BluetoothVehicleData .SOFTWARE_UPDATE_STATE in endpoints else None ,
269+ getParentalControlsState = GetParentalControlsState () if BluetoothVehicleData .PARENTAL_CONTROLS_STATE in endpoints else None ,
270+ )
271+ )
272+ )
273+ )
0 commit comments