tesla-fleet-api 1.0.2__tar.gz → 1.0.3__tar.gz

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.
Files changed (58) hide show
  1. {tesla_fleet_api-1.0.2/tesla_fleet_api.egg-info → tesla_fleet_api-1.0.3}/PKG-INFO +1 -1
  2. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/setup.py +1 -1
  3. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/const.py +15 -1
  4. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/exceptions.py +23 -3
  5. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/bluetooth.py +4 -2
  6. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/partner.py +1 -1
  7. tesla_fleet_api-1.0.3/tesla_fleet_api/tesla/vehicle/bluetooth.py +401 -0
  8. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/commands.py +127 -47
  9. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/signed.py +8 -14
  10. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/vehicles.py +1 -1
  11. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3/tesla_fleet_api.egg-info}/PKG-INFO +1 -1
  12. tesla_fleet_api-1.0.2/tesla_fleet_api/tesla/vehicle/bluetooth.py +0 -226
  13. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/LICENSE +0 -0
  14. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/README.md +0 -0
  15. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/pyproject.toml +0 -0
  16. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/setup.cfg +0 -0
  17. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/__init__.py +0 -0
  18. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/ratecalculator.py +0 -0
  19. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/__init__.py +0 -0
  20. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/charging.py +0 -0
  21. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/energysite.py +0 -0
  22. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/fleet.py +0 -0
  23. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/oauth.py +0 -0
  24. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/tesla.py +0 -0
  25. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/user.py +0 -0
  26. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/__init__.py +0 -0
  27. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/fleet.py +0 -0
  28. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/__init__.py +0 -0
  29. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/__init__.pyi +0 -0
  30. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/car_server_pb2.py +0 -0
  31. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/car_server_pb2.pyi +0 -0
  32. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/common_pb2.py +0 -0
  33. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/common_pb2.pyi +0 -0
  34. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/errors_pb2.py +0 -0
  35. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/errors_pb2.pyi +0 -0
  36. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/keys_pb2.py +0 -0
  37. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/keys_pb2.pyi +0 -0
  38. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/managed_charging_pb2.py +0 -0
  39. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/managed_charging_pb2.pyi +0 -0
  40. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/signatures_pb2.py +0 -0
  41. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/signatures_pb2.pyi +0 -0
  42. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/universal_message_pb2.py +0 -0
  43. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/universal_message_pb2.pyi +0 -0
  44. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/vcsec_pb2.py +0 -0
  45. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/vcsec_pb2.pyi +0 -0
  46. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/vehicle_pb2.py +0 -0
  47. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/proto/vehicle_pb2.pyi +0 -0
  48. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tesla/vehicle/vehicle.py +0 -0
  49. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/teslemetry/__init__.py +0 -0
  50. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/teslemetry/teslemetry.py +0 -0
  51. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/teslemetry/vehicle.py +0 -0
  52. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tessie/__init__.py +0 -0
  53. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tessie/tessie.py +0 -0
  54. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api/tessie/vehicle.py +0 -0
  55. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api.egg-info/SOURCES.txt +0 -0
  56. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api.egg-info/dependency_links.txt +0 -0
  57. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api.egg-info/requires.txt +0 -0
  58. {tesla_fleet_api-1.0.2 → tesla_fleet_api-1.0.3}/tesla_fleet_api.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tesla_fleet_api
3
- Version: 1.0.2
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
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="tesla_fleet_api",
8
- version="1.0.2",
8
+ version="1.0.3",
9
9
  author="Brett Adams",
10
10
  author_email="hello@teslemetry.com",
11
11
  description="Tesla Fleet API library for Python",
@@ -3,7 +3,7 @@
3
3
  from enum import Enum
4
4
  import logging
5
5
 
6
- VERSION = "1.0.2"
6
+ VERSION = "1.0.3"
7
7
  LOGGER = logging.getLogger(__package__)
8
8
  SERVERS = {
9
9
  "na": "https://fleet-api.prd.na.vn.cloud.tesla.com",
@@ -159,3 +159,17 @@ class TeslaEnergyPeriod(StrEnum):
159
159
  MONTH = "month"
160
160
  YEAR = "year"
161
161
  LIFETIME = "lifetime"
162
+
163
+ class BluetoothVehicleData(StrEnum):
164
+ CHARGE_STATE = "GetChargeState"
165
+ CLIMATE_STATE = "GetClimateState"
166
+ DRIVE_STATE = "GetDriveState"
167
+ LOCATION_STATE = "GetLocationState"
168
+ CLOSURES_STATE = "GetClosuresState"
169
+ CHARGE_SCHEDULE_STATE = "GetChargeScheduleState"
170
+ PRECONDITIONING_SCHEDULE_STATE = "GetPreconditioningScheduleState"
171
+ TIRE_PRESSURE_STATE = "GetTirePressureState"
172
+ MEDIA_STATE = "GetMediaState"
173
+ MEDIA_DETAIL_STATE = "GetMediaDetailState"
174
+ SOFTWARE_UPDATE_STATE = "GetSoftwareUpdateState"
175
+ PARENTAL_CONTROLS_STATE = "GetParentalControlsState"
@@ -656,6 +656,26 @@ class TeslaFleetMessageFaultResponseSizeExceedsMTU(TeslaFleetMessageFault):
656
656
  message = "Client's request was received, but response size exceeded MTU"
657
657
  code = 25
658
658
 
659
+ class TeslaFleetMessageFaultRepeatedCounter(TeslaFleetMessageFault):
660
+ """The vehicle has seen this counter value before. Reset the counter and try again"""
661
+
662
+ message = "The vehicle has seen this counter value before. Reset the counter and try again"
663
+ code = 26
664
+
665
+
666
+ class TeslaFleetMessageFaultInvalidKeyHandle(TeslaFleetMessageFault):
667
+ """The key handle is not valid. The key may have been revoked or expired"""
668
+
669
+ message = "The key handle is not valid. The key may have been revoked or expired"
670
+ code = 27
671
+
672
+
673
+ class TeslaFleetMessageFaultRequiresResponseEncryption(TeslaFleetMessageFault):
674
+ """The response requires encryption but encryption was not requested"""
675
+
676
+ message = "The response requires encryption but encryption was not requested"
677
+ code = 28
678
+
659
679
 
660
680
  MESSAGE_FAULTS = [
661
681
  None,
@@ -684,9 +704,9 @@ MESSAGE_FAULTS = [
684
704
  TeslaFleetMessageFaultCommandRequiresAccountCredentials,
685
705
  TeslaFleetMessageFaultFieldExceedsMTU,
686
706
  TeslaFleetMessageFaultResponseSizeExceedsMTU,
687
- None,
688
- None,
689
- None,
707
+ TeslaFleetMessageFaultRepeatedCounter,
708
+ TeslaFleetMessageFaultInvalidKeyHandle,
709
+ TeslaFleetMessageFaultRequiresResponseEncryption,
690
710
  ]
691
711
 
692
712
  class SignedMessageInformationFault(TeslaFleetError):
@@ -2,6 +2,8 @@
2
2
 
3
3
  import hashlib
4
4
  import re
5
+ from bleak.backends.device import BLEDevice
6
+ from cryptography.hazmat.primitives.asymmetric import ec
5
7
 
6
8
  from tesla_fleet_api.tesla.tesla import Tesla
7
9
  from tesla_fleet_api.tesla.vehicle.bluetooth import VehicleBluetooth
@@ -36,8 +38,8 @@ class Vehicles(dict[str, VehicleBluetooth]):
36
38
  """Creates a specific vehicle."""
37
39
  return self.createBluetooth(vin)
38
40
 
39
- def createBluetooth(self, vin: str) -> VehicleBluetooth:
41
+ def createBluetooth(self, vin: str, key: ec.EllipticCurvePrivateKey | None = None, device: None | str | BLEDevice = None) -> VehicleBluetooth:
40
42
  """Creates a specific vehicle."""
41
- vehicle = VehicleBluetooth(self._parent, vin)
43
+ vehicle = VehicleBluetooth(self._parent, vin, key, device)
42
44
  self[vin] = vehicle
43
45
  return vehicle
@@ -1,5 +1,5 @@
1
1
  from typing import Any
2
- from ..const import Method
2
+ from tesla_fleet_api.const import Method
3
3
 
4
4
 
5
5
  class Partner:
@@ -0,0 +1,401 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import asyncio
5
+ from typing import TYPE_CHECKING
6
+ from google.protobuf.message import DecodeError
7
+
8
+ from bleak import BleakClient, BleakScanner
9
+ from bleak.backends.characteristic import BleakGATTCharacteristic
10
+ from bleak.backends.device import BLEDevice
11
+ from cryptography.hazmat.primitives.asymmetric import ec
12
+
13
+ from tesla_fleet_api.tesla.vehicle.proto.keys_pb2 import Role
14
+
15
+ from tesla_fleet_api.tesla.vehicle.commands import Commands
16
+
17
+ from tesla_fleet_api.const import (
18
+ LOGGER,
19
+ BluetoothVehicleData
20
+ )
21
+ from tesla_fleet_api.exceptions import (
22
+ MESSAGE_FAULTS,
23
+ WHITELIST_OPERATION_STATUS,
24
+ WhitelistOperationStatus,
25
+ NotOnWhitelistFault,
26
+ )
27
+
28
+ # Protocol
29
+ from tesla_fleet_api.tesla.vehicle.proto.car_server_pb2 import (
30
+ Action,
31
+ VehicleAction,
32
+ 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,
46
+ )
47
+ from tesla_fleet_api.tesla.vehicle.proto.signatures_pb2 import (
48
+ SessionInfo,
49
+ Session_Info_Status
50
+ )
51
+ from tesla_fleet_api.tesla.vehicle.proto.universal_message_pb2 import (
52
+ Destination,
53
+ Domain,
54
+ RoutableMessage,
55
+ )
56
+ from tesla_fleet_api.tesla.vehicle.proto.vcsec_pb2 import (
57
+ FromVCSECMessage,
58
+ InformationRequest,
59
+ InformationRequestType,
60
+ KeyFormFactor,
61
+ KeyMetadata,
62
+ PermissionChange,
63
+ PublicKey,
64
+ RKEAction_E,
65
+ UnsignedMessage,
66
+ VehicleStatus,
67
+ WhitelistOperation,
68
+ )
69
+ from tesla_fleet_api.tesla.vehicle.proto.vehicle_pb2 import ChargeScheduleState, ChargeState, ClimateState, ClosuresState, DriveState, LocationState, MediaDetailState, MediaState, ParentalControlsState, PreconditioningScheduleState, SoftwareUpdateState, TirePressureState, VehicleData
70
+
71
+ SERVICE_UUID = "00000211-b2d1-43f0-9b88-960cebf8b91e"
72
+ WRITE_UUID = "00000212-b2d1-43f0-9b88-960cebf8b91e"
73
+ READ_UUID = "00000213-b2d1-43f0-9b88-960cebf8b91e"
74
+ VERSION_UUID = "00000214-b2d1-43f0-9b88-960cebf8b91e"
75
+
76
+ if TYPE_CHECKING:
77
+ from tesla_fleet_api.tesla.tesla import Tesla
78
+
79
+ def prependLength(message: bytes) -> bytearray:
80
+ """Prepend a 2-byte length to the payload."""
81
+ return bytearray([len(message) >> 8, len(message) & 0xFF]) + message
82
+
83
+ class VehicleBluetooth(Commands):
84
+ """Class describing the Tesla Fleet API vehicle endpoints and commands for a specific vehicle with command signing."""
85
+
86
+ ble_name: str
87
+ client: BleakClient
88
+ _queues: dict[Domain, asyncio.Queue]
89
+ _ekey: ec.EllipticCurvePublicKey
90
+ _recv: bytearray = bytearray()
91
+ _recv_len: int = 0
92
+ _auth_method = "aes"
93
+
94
+ def __init__(
95
+ self, parent: Tesla, vin: str, key: ec.EllipticCurvePrivateKey | None = None, device: None | str | BLEDevice = None
96
+ ):
97
+ super().__init__(parent, vin, key)
98
+ self.ble_name = "S" + hashlib.sha1(vin.encode('utf-8')).hexdigest()[:16] + "C"
99
+ self._queues = {
100
+ Domain.DOMAIN_VEHICLE_SECURITY: asyncio.Queue(),
101
+ Domain.DOMAIN_INFOTAINMENT: asyncio.Queue(),
102
+ }
103
+ if device is not None:
104
+ self.client = BleakClient(device, services=[SERVICE_UUID])
105
+
106
+ async def find_client(self, scanner: BleakScanner = BleakScanner()) -> BleakClient:
107
+ """Find the Tesla BLE device."""
108
+
109
+ device = await scanner.find_device_by_name(self.ble_name)
110
+ if not device:
111
+ raise ValueError(f"Device {self.ble_name} not found")
112
+ self.client = BleakClient(device, services=[SERVICE_UUID])
113
+ LOGGER.debug(f"Discovered device {device.name} {device.address}")
114
+ return self.client
115
+
116
+ def create_client(self, device: str|BLEDevice) -> BleakClient:
117
+ """Create a client using a MAC address or Bleak Device."""
118
+ self.client = BleakClient(device, services=[SERVICE_UUID])
119
+ return self.client
120
+
121
+ async def connect(self, device: str|BLEDevice | None = None) -> None:
122
+ """Connect to the Tesla BLE device."""
123
+ if device is not None:
124
+ self.create_client(device)
125
+ await self.client.connect()
126
+ await self.client.start_notify(READ_UUID, self._on_notify)
127
+
128
+ async def disconnect(self) -> bool:
129
+ """Disconnect from the Tesla BLE device."""
130
+ return await self.client.disconnect()
131
+
132
+ async def __aenter__(self) -> VehicleBluetooth:
133
+ """Enter the async context."""
134
+ await self.connect()
135
+ return self
136
+
137
+ async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
138
+ """Exit the async context."""
139
+ await self.disconnect()
140
+
141
+ async def _on_notify(self,sender: BleakGATTCharacteristic,data : bytearray) -> None:
142
+ """Receive data from the Tesla BLE device."""
143
+ if self._recv_len:
144
+ self._recv += data
145
+ else:
146
+ self._recv_len = int.from_bytes(data[:2], 'big')
147
+ self._recv = data[2:]
148
+ LOGGER.debug(f"Received {len(self._recv)} of {self._recv_len} bytes")
149
+ while len(self._recv) > self._recv_len:
150
+ LOGGER.warn(f"Received more data than expected: {len(self._recv)} > {self._recv_len}")
151
+ await self._on_message(bytes(self._recv[:self._recv_len]))
152
+ self._recv_len = int.from_bytes(self._recv[self._recv_len:self._recv_len+2], 'big')
153
+ self._recv = self._recv[self._recv_len+2:]
154
+ continue
155
+ if len(self._recv) == self._recv_len:
156
+ await self._on_message(bytes(self._recv))
157
+ self._recv = bytearray()
158
+ self._recv_len = 0
159
+
160
+ async def _on_message(self, data:bytes) -> None:
161
+ """Receive messages from the Tesla BLE data."""
162
+ try:
163
+ msg = RoutableMessage.FromString(data)
164
+ except DecodeError as e:
165
+ LOGGER.error(f"Error parsing message: {e}")
166
+ self._recv = bytearray()
167
+ self._recv_len = 0
168
+ return
169
+
170
+ if(msg.to_destination.routing_address != self._from_destination):
171
+ # Ignore ephemeral key broadcasts
172
+ return
173
+
174
+ LOGGER.info(f"Received response: {msg}")
175
+ await self._queues[msg.from_destination.domain].put(msg)
176
+
177
+ async def _send(self, msg: RoutableMessage, requires: str) -> RoutableMessage:
178
+ """Serialize a message and send to the vehicle and wait for a response."""
179
+ domain = msg.to_destination.domain
180
+ async with self._sessions[domain].lock:
181
+ LOGGER.debug(f"Sending message {msg}")
182
+
183
+ payload = prependLength(msg.SerializeToString())
184
+
185
+ # Empty the queue before sending the message
186
+ while not self._queues[domain].empty():
187
+ await self._queues[domain].get()
188
+ await self.client.write_gatt_char(WRITE_UUID, payload, True)
189
+
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}")
195
+
196
+ self.validate_msg(resp)
197
+
198
+ if resp.HasField(requires):
199
+ return resp
200
+
201
+ async def pair(self, role: Role = Role.ROLE_OWNER, form: KeyFormFactor = KeyFormFactor.KEY_FORM_FACTOR_CLOUD_KEY):
202
+ """Pair the key."""
203
+
204
+ request = UnsignedMessage(
205
+ WhitelistOperation=WhitelistOperation(
206
+ addKeyToWhitelistAndAddPermissions=PermissionChange(
207
+ key=PublicKey(PublicKeyRaw=self._public_key),
208
+ keyRole=role
209
+ ),
210
+ metadataForKey=KeyMetadata(
211
+ keyFormFactor=form
212
+ )
213
+ )
214
+ )
215
+ msg = RoutableMessage(
216
+ to_destination=Destination(
217
+ domain=Domain.DOMAIN_VEHICLE_SECURITY
218
+ ),
219
+ from_destination=Destination(
220
+ routing_address=self._from_destination
221
+ ),
222
+ protobuf_message_as_bytes=request.SerializeToString(),
223
+ )
224
+ resp = await self._send(msg)
225
+ respMsg = FromVCSECMessage.FromString(resp.protobuf_message_as_bytes)
226
+ if(respMsg.commandStatus.whitelistOperationStatus.whitelistOperationInformation):
227
+ if(respMsg.commandStatus.whitelistOperationStatus.whitelistOperationInformation < len(WHITELIST_OPERATION_STATUS)):
228
+ raise WHITELIST_OPERATION_STATUS[respMsg.commandStatus.whitelistOperationStatus.whitelistOperationInformation]
229
+ else:
230
+ raise WhitelistOperationStatus(f"Unknown whitelist operation failure: {respMsg.commandStatus.whitelistOperationStatus.whitelistOperationInformation}")
231
+ return
232
+
233
+ async def wake_up(self):
234
+ """Wake up the vehicle."""
235
+ return await self._sendVehicleSecurity(
236
+ UnsignedMessage(RKEAction=RKEAction_E.RKE_ACTION_WAKE_VEHICLE)
237
+ )
238
+
239
+ async def vehicle_data(self, endpoints: list[BluetoothVehicleData]) -> VehicleData:
240
+ """Get vehicle data."""
241
+ return await self._getInfotainment(
242
+ Action(
243
+ vehicleAction=VehicleAction(
244
+ getVehicleData=GetVehicleData(
245
+ getChargeState = GetChargeState() if BluetoothVehicleData.CHARGE_STATE in endpoints else None,
246
+ getClimateState = GetClimateState() if BluetoothVehicleData.CLIMATE_STATE in endpoints else None,
247
+ getDriveState = GetDriveState() if BluetoothVehicleData.DRIVE_STATE in endpoints else None,
248
+ getLocationState = GetLocationState() if BluetoothVehicleData.LOCATION_STATE in endpoints else None,
249
+ getClosuresState = GetClosuresState() if BluetoothVehicleData.CLOSURES_STATE in endpoints else None,
250
+ getChargeScheduleState = GetChargeScheduleState() if BluetoothVehicleData.CHARGE_SCHEDULE_STATE in endpoints else None,
251
+ getPreconditioningScheduleState = GetPreconditioningScheduleState() if BluetoothVehicleData.PRECONDITIONING_SCHEDULE_STATE in endpoints else None,
252
+ getTirePressureState = GetTirePressureState() if BluetoothVehicleData.TIRE_PRESSURE_STATE in endpoints else None,
253
+ getMediaState = GetMediaState() if BluetoothVehicleData.MEDIA_STATE in endpoints else None,
254
+ getMediaDetailState = GetMediaDetailState() if BluetoothVehicleData.MEDIA_DETAIL_STATE in endpoints else None,
255
+ getSoftwareUpdateState = GetSoftwareUpdateState() if BluetoothVehicleData.SOFTWARE_UPDATE_STATE in endpoints else None,
256
+ getParentalControlsState = GetParentalControlsState() if BluetoothVehicleData.PARENTAL_CONTROLS_STATE in endpoints else None,
257
+ )
258
+ )
259
+ )
260
+ )
261
+
262
+ async def charge_state(self) -> ChargeState:
263
+ return (await self._getInfotainment(
264
+ Action(
265
+ vehicleAction=VehicleAction(
266
+ getVehicleData=GetVehicleData(
267
+ getChargeState=GetChargeState()
268
+ )
269
+ )
270
+ )
271
+ )).charge_state
272
+
273
+ async def climate_state(self) -> ClimateState:
274
+ return (await self._getInfotainment(
275
+ Action(
276
+ vehicleAction=VehicleAction(
277
+ getVehicleData=GetVehicleData(
278
+ getClimateState=GetClimateState()
279
+ )
280
+ )
281
+ )
282
+ )).climate_state
283
+
284
+ async def drive_state(self) -> DriveState:
285
+ return (await self._getInfotainment(
286
+ Action(
287
+ vehicleAction=VehicleAction(
288
+ getVehicleData=GetVehicleData(
289
+ getDriveState=GetDriveState()
290
+ )
291
+ )
292
+ )
293
+ )).drive_state
294
+
295
+ async def location_state(self) -> LocationState:
296
+ return (await self._getInfotainment(
297
+ Action(
298
+ vehicleAction=VehicleAction(
299
+ getVehicleData=GetVehicleData(
300
+ getLocationState=GetLocationState()
301
+ )
302
+ )
303
+ )
304
+ )).location_state
305
+
306
+ async def closures_state(self) -> ClosuresState:
307
+ return (await self._getInfotainment(
308
+ Action(
309
+ vehicleAction=VehicleAction(
310
+ getVehicleData=GetVehicleData(
311
+ getClosuresState=GetClosuresState()
312
+ )
313
+ )
314
+ )
315
+ )).closures_state
316
+
317
+ async def charge_schedule_state(self) -> ChargeScheduleState:
318
+ return (await self._getInfotainment(
319
+ Action(
320
+ vehicleAction=VehicleAction(
321
+ getVehicleData=GetVehicleData(
322
+ getChargeScheduleState=GetChargeScheduleState()
323
+ )
324
+ )
325
+ )
326
+ )).charge_schedule_state
327
+
328
+ async def preconditioning_schedule_state(self) -> PreconditioningScheduleState:
329
+ return (await self._getInfotainment(
330
+ Action(
331
+ vehicleAction=VehicleAction(
332
+ getVehicleData=GetVehicleData(
333
+ getPreconditioningScheduleState=GetPreconditioningScheduleState()
334
+ )
335
+ )
336
+ )
337
+ )).preconditioning_schedule_state
338
+
339
+ async def tire_pressure_state(self) -> TirePressureState:
340
+ return (await self._getInfotainment(
341
+ Action(
342
+ vehicleAction=VehicleAction(
343
+ getVehicleData=GetVehicleData(
344
+ getTirePressureState=GetTirePressureState()
345
+ )
346
+ )
347
+ )
348
+ )).tire_pressure_state
349
+
350
+ async def media_state(self) -> MediaState:
351
+ return (await self._getInfotainment(
352
+ Action(
353
+ vehicleAction=VehicleAction(
354
+ getVehicleData=GetVehicleData(
355
+ getMediaState=GetMediaState()
356
+ )
357
+ )
358
+ )
359
+ )).media_state
360
+
361
+ async def media_detail_state(self) -> MediaDetailState:
362
+ return (await self._getInfotainment(
363
+ Action(
364
+ vehicleAction=VehicleAction(
365
+ getVehicleData=GetVehicleData(
366
+ getMediaDetailState=GetMediaDetailState()
367
+ )
368
+ )
369
+ )
370
+ )).media_detail_state
371
+
372
+ async def software_update_state(self) -> SoftwareUpdateState:
373
+ return (await self._getInfotainment(
374
+ Action(
375
+ vehicleAction=VehicleAction(
376
+ getVehicleData=GetVehicleData(
377
+ getSoftwareUpdateState=GetSoftwareUpdateState()
378
+ )
379
+ )
380
+ )
381
+ )).software_update_state
382
+
383
+ async def parental_controls_state(self) -> ParentalControlsState:
384
+ return (await self._getInfotainment(
385
+ Action(
386
+ vehicleAction=VehicleAction(
387
+ getVehicleData=GetVehicleData(
388
+ getParentalControlsState=GetParentalControlsState()
389
+ )
390
+ )
391
+ )
392
+ )).parental_controls_state
393
+
394
+ async def vehicle_state(self) -> VehicleStatus:
395
+ return await self._getVehicleSecurity(
396
+ UnsignedMessage(
397
+ InformationRequest=InformationRequest(
398
+ informationRequestType=InformationRequestType.INFORMATION_REQUEST_TYPE_GET_STATUS
399
+ )
400
+ )
401
+ )