tesla-fleet-api 1.0.11__py3-none-any.whl → 1.0.13__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 +11 -3
- tesla_fleet_api/tesla/__init__.py +14 -0
- tesla_fleet_api/tesla/fleet.py +1 -2
- tesla_fleet_api/tesla/vehicle/__init__.py +2 -1
- tesla_fleet_api/tesla/vehicle/bluetooth.py +90 -43
- tesla_fleet_api/tesla/vehicle/commands.py +0 -1
- tesla_fleet_api/tesla/vehicle/vehicles.py +1 -5
- tesla_fleet_api/teslemetry/__init__.py +9 -0
- tesla_fleet_api/teslemetry/vehicles.py +10 -15
- tesla_fleet_api/tessie/__init__.py +9 -0
- tesla_fleet_api/tessie/vehicles.py +10 -15
- tesla_fleet_api-1.0.13.dist-info/METADATA +254 -0
- {tesla_fleet_api-1.0.11.dist-info → tesla_fleet_api-1.0.13.dist-info}/RECORD +16 -16
- {tesla_fleet_api-1.0.11.dist-info → tesla_fleet_api-1.0.13.dist-info}/WHEEL +1 -1
- tesla_fleet_api-1.0.11.dist-info/METADATA +0 -158
- {tesla_fleet_api-1.0.11.dist-info → tesla_fleet_api-1.0.13.dist-info}/LICENSE +0 -0
- {tesla_fleet_api-1.0.11.dist-info → tesla_fleet_api-1.0.13.dist-info}/top_level.txt +0 -0
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.
|
6
|
+
VERSION = "1.0.13"
|
7
7
|
LOGGER = logging.getLogger(__package__)
|
8
8
|
SERVERS = {
|
9
9
|
"na": "https://fleet-api.prd.na.vn.cloud.tesla.com",
|
@@ -47,10 +47,18 @@ class Seat(IntEnum):
|
|
47
47
|
FRONT_LEFT = 0
|
48
48
|
FRONT_RIGHT = 1
|
49
49
|
REAR_LEFT = 2
|
50
|
+
REAT_LEFT_BACK = 3
|
50
51
|
REAR_CENTER = 4
|
51
52
|
REAR_RIGHT = 5
|
52
|
-
|
53
|
-
|
53
|
+
REAR_RIGHT_BACK = 6
|
54
|
+
THIRD_LEFT = 7
|
55
|
+
THIRD_RIGHT = 8
|
56
|
+
|
57
|
+
class AutoSeat(IntEnum):
|
58
|
+
"""Auto Climate Seat positions"""
|
59
|
+
|
60
|
+
FRONT_LEFT = 1
|
61
|
+
FRONT_RIGHT = 2
|
54
62
|
|
55
63
|
|
56
64
|
class Level(IntEnum):
|
@@ -3,9 +3,23 @@
|
|
3
3
|
from tesla_fleet_api.tesla.fleet import TeslaFleetApi
|
4
4
|
from tesla_fleet_api.tesla.bluetooth import TeslaBluetooth
|
5
5
|
from tesla_fleet_api.tesla.oauth import TeslaFleetOAuth
|
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 import Vehicles, VehiclesBluetooth, VehicleFleet, VehicleSigned, VehicleBluetooth
|
6
11
|
|
7
12
|
__all__ = [
|
8
13
|
"TeslaFleetApi",
|
9
14
|
"TeslaBluetooth",
|
10
15
|
"TeslaFleetOAuth",
|
16
|
+
"Charging",
|
17
|
+
"EnergySites",
|
18
|
+
"Partner",
|
19
|
+
"User",
|
20
|
+
"Vehicles",
|
21
|
+
"VehiclesBluetooth",
|
22
|
+
"VehicleFleet",
|
23
|
+
"VehicleSigned",
|
24
|
+
"VehicleBluetooth"
|
11
25
|
]
|
tesla_fleet_api/tesla/fleet.py
CHANGED
@@ -115,8 +115,7 @@ class TeslaFleetApi(Tesla):
|
|
115
115
|
json=json,
|
116
116
|
params=params,
|
117
117
|
) as resp:
|
118
|
-
LOGGER.debug("
|
119
|
-
LOGGER.debug("Response Status: %s", resp.status)
|
118
|
+
LOGGER.debug("Status %s from %s", resp.status, resp.url)
|
120
119
|
if "x-txid" in resp.headers:
|
121
120
|
LOGGER.debug("Response TXID: %s", resp.headers["x-txid"])
|
122
121
|
if "RateLimit-Reset" in resp.headers:
|
@@ -1,12 +1,13 @@
|
|
1
1
|
"""Tesla Fleet API classes."""
|
2
2
|
|
3
|
-
from tesla_fleet_api.tesla.vehicle.vehicles import Vehicles
|
3
|
+
from tesla_fleet_api.tesla.vehicle.vehicles import Vehicles, VehiclesBluetooth
|
4
4
|
from tesla_fleet_api.tesla.vehicle.fleet import VehicleFleet
|
5
5
|
from tesla_fleet_api.tesla.vehicle.bluetooth import VehicleBluetooth
|
6
6
|
from tesla_fleet_api.tesla.vehicle.signed import VehicleSigned
|
7
7
|
|
8
8
|
__all__ = [
|
9
9
|
"Vehicles",
|
10
|
+
"VehiclesBluetooth",
|
10
11
|
"VehicleFleet",
|
11
12
|
"VehicleBluetooth",
|
12
13
|
"VehicleSigned",
|
@@ -2,7 +2,8 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import hashlib
|
4
4
|
import asyncio
|
5
|
-
|
5
|
+
import struct
|
6
|
+
from typing import TYPE_CHECKING, Callable, Any
|
6
7
|
from google.protobuf.message import DecodeError
|
7
8
|
from bleak_retry_connector import establish_connection, MAX_CONNECT_ATTEMPTS
|
8
9
|
from bleak import BleakClient, BleakScanner
|
@@ -75,6 +76,70 @@ def prependLength(message: bytes) -> bytearray:
|
|
75
76
|
"""Prepend a 2-byte length to the payload."""
|
76
77
|
return bytearray([len(message) >> 8, len(message) & 0xFF]) + message
|
77
78
|
|
79
|
+
class ReassemblingBuffer:
|
80
|
+
"""
|
81
|
+
Reassembles bytearray streams where the first two bytes indicate the length of the message.
|
82
|
+
Handles potential packet corruption by discarding *entire* packets and retrying.
|
83
|
+
Uses a callback to process parsed messages.
|
84
|
+
"""
|
85
|
+
|
86
|
+
def __init__(self, callback: Callable[[RoutableMessage], None]):
|
87
|
+
"""
|
88
|
+
Initializes the buffer.
|
89
|
+
|
90
|
+
Args:
|
91
|
+
message_type: The protobuf message type (e.g., RoutableMessage) to parse the assembled data.
|
92
|
+
callback: A function that will be called with each parsed message.
|
93
|
+
"""
|
94
|
+
self.buffer = bytearray()
|
95
|
+
self.expected_length = None
|
96
|
+
self.packet_starts = []
|
97
|
+
self.callback = callback
|
98
|
+
|
99
|
+
def receive_data(self, data: bytearray):
|
100
|
+
"""
|
101
|
+
Receives a chunk of bytearray data and attempts to assemble a complete message.
|
102
|
+
|
103
|
+
Args:
|
104
|
+
data: The received bytearray data.
|
105
|
+
"""
|
106
|
+
self.packet_starts.append(len(self.buffer))
|
107
|
+
self.buffer.extend(data)
|
108
|
+
|
109
|
+
while True:
|
110
|
+
if self.expected_length is None and len(self.buffer) >= 2:
|
111
|
+
self.expected_length = struct.unpack(">H", self.buffer[:2])[0] + 2
|
112
|
+
|
113
|
+
LOGGER.info(f"Buffer length: {len(self.buffer)}, Packet starts: {self.packet_starts}, Expected length: {self.expected_length}")
|
114
|
+
|
115
|
+
if self.expected_length is not None and self.expected_length > 1024:
|
116
|
+
LOGGER.warning(f"Expected length too large: {self.expected_length}")
|
117
|
+
self.discard_packet()
|
118
|
+
|
119
|
+
elif self.expected_length is not None and len(self.buffer) >= self.expected_length:
|
120
|
+
try:
|
121
|
+
message = RoutableMessage()
|
122
|
+
message.ParseFromString(bytes(self.buffer[2:self.expected_length]))
|
123
|
+
self.buffer = self.buffer[self.expected_length:]
|
124
|
+
self.packet_starts = [x - self.expected_length for x in self.packet_starts if x >= self.expected_length]
|
125
|
+
self.expected_length = None
|
126
|
+
self.callback(message) # Call the callback with the parsed message
|
127
|
+
|
128
|
+
except DecodeError:
|
129
|
+
self.discard_packet()
|
130
|
+
else:
|
131
|
+
return
|
132
|
+
|
133
|
+
def discard_packet(self):
|
134
|
+
self.packet_starts.pop(0)
|
135
|
+
if len(self.packet_starts) > 0:
|
136
|
+
self.buffer = self.buffer[self.packet_starts[0]:]
|
137
|
+
self.packet_starts = [x - self.packet_starts[0] for x in self.packet_starts]
|
138
|
+
else:
|
139
|
+
self.buffer = bytearray()
|
140
|
+
self.packet_starts = []
|
141
|
+
self.expected_length = None
|
142
|
+
|
78
143
|
class VehicleBluetooth(Commands):
|
79
144
|
"""Class describing the Tesla Fleet API vehicle endpoints and commands for a specific vehicle with command signing."""
|
80
145
|
|
@@ -83,8 +148,7 @@ class VehicleBluetooth(Commands):
|
|
83
148
|
client: BleakClient | None = None
|
84
149
|
_queues: dict[Domain, asyncio.Queue]
|
85
150
|
_ekey: ec.EllipticCurvePublicKey
|
86
|
-
|
87
|
-
_recv_len: int = 0
|
151
|
+
_buffer: ReassemblingBuffer
|
88
152
|
_auth_method = "aes"
|
89
153
|
|
90
154
|
def __init__(
|
@@ -98,6 +162,7 @@ class VehicleBluetooth(Commands):
|
|
98
162
|
}
|
99
163
|
self.device = device
|
100
164
|
self._connect_lock = asyncio.Lock()
|
165
|
+
self._buffer = ReassemblingBuffer(self._on_message)
|
101
166
|
|
102
167
|
async def find_vehicle(self, name: str | None = None, address: str | None = None, scanner: BleakScanner | None = None) -> BLEDevice:
|
103
168
|
"""Find the Tesla BLE device."""
|
@@ -146,6 +211,7 @@ class VehicleBluetooth(Commands):
|
|
146
211
|
"""Connect to the Tesla BLE device if not already connected."""
|
147
212
|
async with self._connect_lock:
|
148
213
|
if not self.client or not self.client.is_connected:
|
214
|
+
LOGGER.info(f"Reconnecting to {self.ble_name}")
|
149
215
|
await self.connect(max_attempts=max_attempts)
|
150
216
|
|
151
217
|
async def __aenter__(self) -> VehicleBluetooth:
|
@@ -157,63 +223,36 @@ class VehicleBluetooth(Commands):
|
|
157
223
|
"""Exit the async context."""
|
158
224
|
await self.disconnect()
|
159
225
|
|
160
|
-
|
226
|
+
def _on_notify(self,sender: BleakGATTCharacteristic, data: bytearray) -> None:
|
161
227
|
"""Receive data from the Tesla BLE device."""
|
162
|
-
if
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
self._recv = bytearray()
|
169
|
-
self._recv_len = 0
|
170
|
-
return
|
171
|
-
self._recv = data[2:]
|
172
|
-
#while len(self._recv) > self._recv_len:
|
173
|
-
#
|
174
|
-
# # Maybe this needs to trigger a reset
|
175
|
-
# await self._on_message(bytes(self._recv[:self._recv_len]))
|
176
|
-
# self._recv_len = int.from_bytes(self._recv[self._recv_len:self._recv_len+2], 'big')
|
177
|
-
# self._recv = self._recv[self._recv_len+2:]
|
178
|
-
# continue
|
179
|
-
if len(self._recv) >= self._recv_len:
|
180
|
-
if len(self._recv) > self._recv_len:
|
181
|
-
LOGGER.debug(f"Received more data than expected: {len(self._recv)} > {self._recv_len}")
|
182
|
-
try:
|
183
|
-
msg = RoutableMessage.FromString(bytes(self._recv[:self._recv_len]))
|
184
|
-
await self._on_message(msg)
|
185
|
-
self._recv = bytearray()
|
186
|
-
self._recv_len = 0
|
187
|
-
except DecodeError:
|
188
|
-
# Attempt parsing the whole payload
|
189
|
-
msg = RoutableMessage.FromString(bytes(self._recv))
|
190
|
-
LOGGER.warn(f"Parsed more data than length: {len(self._recv)} > {self._recv_len}")
|
191
|
-
await self._on_message(msg)
|
192
|
-
self._recv = bytearray()
|
193
|
-
self._recv_len = 0
|
194
|
-
|
195
|
-
async def _on_message(self, msg: RoutableMessage) -> None:
|
228
|
+
if sender.uuid != READ_UUID:
|
229
|
+
LOGGER.error(f"Unexpected sender: {sender}")
|
230
|
+
return
|
231
|
+
self._buffer.receive_data(data)
|
232
|
+
|
233
|
+
def _on_message(self, msg: RoutableMessage) -> None:
|
196
234
|
"""Receive messages from the Tesla BLE data."""
|
197
235
|
|
198
236
|
if(msg.to_destination.routing_address != self._from_destination):
|
199
237
|
# Ignore ephemeral key broadcasts
|
200
238
|
return
|
201
239
|
|
202
|
-
LOGGER.
|
203
|
-
|
240
|
+
LOGGER.info(f"Received response: {msg}")
|
241
|
+
self._queues[msg.from_destination.domain].put_nowait(msg)
|
204
242
|
|
205
|
-
async def _send(self, msg: RoutableMessage, requires: str, timeout: int =
|
243
|
+
async def _send(self, msg: RoutableMessage, requires: str, timeout: int = 5) -> RoutableMessage:
|
206
244
|
"""Serialize a message and send to the vehicle and wait for a response."""
|
207
245
|
|
208
246
|
domain = msg.to_destination.domain
|
209
247
|
async with self._sessions[domain].lock:
|
210
|
-
LOGGER.
|
248
|
+
LOGGER.info(f"Sending message {msg}")
|
211
249
|
|
212
250
|
payload = prependLength(msg.SerializeToString())
|
213
251
|
|
214
252
|
# Empty the queue before sending the message
|
215
253
|
while not self._queues[domain].empty():
|
216
|
-
await self._queues[domain].get()
|
254
|
+
msg = await self._queues[domain].get()
|
255
|
+
LOGGER.warning(f"Discarded message {msg}")
|
217
256
|
|
218
257
|
await self.connect_if_needed()
|
219
258
|
assert self.client is not None
|
@@ -221,6 +260,7 @@ class VehicleBluetooth(Commands):
|
|
221
260
|
|
222
261
|
# Process the response
|
223
262
|
async with asyncio.timeout(timeout):
|
263
|
+
LOGGER.info(f"Waiting for response with {requires}")
|
224
264
|
while True:
|
225
265
|
resp = await self._queues[domain].get()
|
226
266
|
LOGGER.debug(f"Received message {resp}")
|
@@ -229,6 +269,8 @@ class VehicleBluetooth(Commands):
|
|
229
269
|
|
230
270
|
if resp.HasField(requires):
|
231
271
|
return resp
|
272
|
+
else:
|
273
|
+
LOGGER.warning(f"Ignoring message since it does not contain the required field {requires}, {resp.HasField(requires)}")
|
232
274
|
|
233
275
|
async def query_display_name(self, max_attempts=5) -> str | None:
|
234
276
|
"""Read the device name via GATT characteristic if available"""
|
@@ -270,6 +312,11 @@ class VehicleBluetooth(Commands):
|
|
270
312
|
LOGGER.error(f"Failed to read device version: {e}")
|
271
313
|
return None
|
272
314
|
|
315
|
+
async def _command(self, domain: Domain, command: bytes, attempt: int = 0) -> dict[str, Any]:
|
316
|
+
"""Serialize a message and send to the signed command endpoint."""
|
317
|
+
await self.connect_if_needed()
|
318
|
+
return await super()._command(domain, command, attempt)
|
319
|
+
|
273
320
|
async def pair(self, role: Role = Role.ROLE_OWNER, form: KeyFormFactor = KeyFormFactor.KEY_FORM_FACTOR_CLOUD_KEY, timeout: int = 60):
|
274
321
|
"""Pair the key."""
|
275
322
|
|
@@ -269,7 +269,6 @@ class Commands(Vehicle):
|
|
269
269
|
if msg.signedMessageStatus.signed_message_fault > 0:
|
270
270
|
raise MESSAGE_FAULTS[msg.signedMessageStatus.signed_message_fault]
|
271
271
|
|
272
|
-
@abstractmethod
|
273
272
|
async def _command(self, domain: Domain, command: bytes, attempt: int = 0) -> dict[str, Any]:
|
274
273
|
"""Serialize a message and send to the signed command endpoint."""
|
275
274
|
session = self._sessions[domain]
|
@@ -1,16 +1,12 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
import asyncio
|
3
2
|
from typing import TYPE_CHECKING
|
4
|
-
from bleak import BleakClient
|
5
3
|
from bleak.backends.device import BLEDevice
|
6
|
-
from bleak_retry_connector import establish_connection
|
7
4
|
from cryptography.hazmat.primitives.asymmetric import ec
|
8
5
|
|
9
6
|
from tesla_fleet_api.tesla.vehicle.signed import VehicleSigned
|
10
|
-
from tesla_fleet_api.tesla.vehicle.bluetooth import
|
7
|
+
from tesla_fleet_api.tesla.vehicle.bluetooth import VehicleBluetooth
|
11
8
|
from tesla_fleet_api.tesla.vehicle.fleet import VehicleFleet
|
12
9
|
from tesla_fleet_api.tesla.vehicle.vehicle import Vehicle
|
13
|
-
from tesla_fleet_api.const import LOGGER
|
14
10
|
|
15
11
|
if TYPE_CHECKING:
|
16
12
|
from tesla_fleet_api.tesla.fleet import TeslaFleetApi
|
@@ -1,5 +1,14 @@
|
|
1
1
|
from tesla_fleet_api.teslemetry.teslemetry import Teslemetry
|
2
|
+
from tesla_fleet_api.tesla.charging import Charging
|
3
|
+
from tesla_fleet_api.tesla.energysite import EnergySites
|
4
|
+
from tesla_fleet_api.tesla.user import User
|
5
|
+
from tesla_fleet_api.teslemetry.vehicles import TeslemetryVehicles as Vehicles, TeslemetryVehicle as Vehicle
|
2
6
|
|
3
7
|
__all__ = [
|
4
8
|
"Teslemetry",
|
9
|
+
"Charging",
|
10
|
+
"EnergySites",
|
11
|
+
"User",
|
12
|
+
"Vehicles",
|
13
|
+
"Vehicle",
|
5
14
|
]
|
@@ -2,18 +2,13 @@ from __future__ import annotations
|
|
2
2
|
from typing import TYPE_CHECKING, Any
|
3
3
|
|
4
4
|
from tesla_fleet_api.const import Method
|
5
|
-
from tesla_fleet_api.tesla.vehicle.vehicle import Vehicle
|
6
5
|
from tesla_fleet_api.tesla.vehicle.vehicles import Vehicles
|
7
6
|
from tesla_fleet_api.tesla.vehicle.fleet import VehicleFleet
|
8
7
|
|
9
8
|
if TYPE_CHECKING:
|
10
9
|
pass
|
11
10
|
|
12
|
-
class TeslemetryVehicle(
|
13
|
-
"""Teslemetry specific base vehicle."""
|
14
|
-
pass
|
15
|
-
|
16
|
-
class TeslemetryVehicleFleet(VehicleFleet):
|
11
|
+
class TeslemetryVehicle(VehicleFleet):
|
17
12
|
"""Teslemetry specific API vehicle."""
|
18
13
|
|
19
14
|
async def server_side_polling(
|
@@ -52,22 +47,22 @@ class TeslemetryVehicleFleet(VehicleFleet):
|
|
52
47
|
class TeslemetryVehicles(Vehicles):
|
53
48
|
"""Class containing and creating vehicles."""
|
54
49
|
|
55
|
-
|
56
|
-
|
57
|
-
def create(self, vin: str) -> TeslemetryVehicleFleet:
|
58
|
-
"""Creates a specific vehicle."""
|
59
|
-
return self.createFleet(vin)
|
50
|
+
Vehicle = TeslemetryVehicle
|
60
51
|
|
61
|
-
def
|
52
|
+
def create(self, vin: str) -> TeslemetryVehicle:
|
62
53
|
"""Creates a specific vehicle."""
|
63
|
-
vehicle = self.
|
54
|
+
vehicle = self.Vehicle(self._parent, vin)
|
64
55
|
self[vin] = vehicle
|
65
56
|
return vehicle
|
66
57
|
|
58
|
+
def createFleet(self, vin: str):
|
59
|
+
"""Creates a specific vehicle."""
|
60
|
+
raise NotImplementedError("Teslemetry cannot use Fleet API directly")
|
61
|
+
|
67
62
|
def createSigned(self, vin: str):
|
68
63
|
"""Creates a specific vehicle."""
|
69
|
-
raise NotImplementedError("
|
64
|
+
raise NotImplementedError("Teslemetry cannot use Fleet API directly")
|
70
65
|
|
71
66
|
def createBluetooth(self, vin: str):
|
72
67
|
"""Creates a specific vehicle."""
|
73
|
-
raise NotImplementedError("
|
68
|
+
raise NotImplementedError("Teslemetry cannot use local Bluetooth")
|
@@ -1,5 +1,14 @@
|
|
1
1
|
from tesla_fleet_api.tessie.tessie import Tessie
|
2
|
+
from tesla_fleet_api.tesla.charging import Charging
|
3
|
+
from tesla_fleet_api.tesla.energysite import EnergySites
|
4
|
+
from tesla_fleet_api.tesla.user import User
|
5
|
+
from tesla_fleet_api.tessie.vehicles import TessieVehicles as Vehicles, TessieVehicle as Vehicle
|
2
6
|
|
3
7
|
__all__ = [
|
4
8
|
"Tessie",
|
9
|
+
"Charging",
|
10
|
+
"EnergySites",
|
11
|
+
"User",
|
12
|
+
"Vehicles",
|
13
|
+
"Vehicle",
|
5
14
|
]
|
@@ -1,37 +1,32 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from tesla_fleet_api.tesla.vehicle.vehicle import Vehicle
|
4
3
|
from tesla_fleet_api.tesla.vehicle.vehicles import Vehicles
|
5
4
|
from tesla_fleet_api.tesla.vehicle.fleet import VehicleFleet
|
6
5
|
|
7
6
|
|
8
|
-
class TessieVehicle(
|
9
|
-
"""Tessie specific base vehicle."""
|
10
|
-
pass
|
11
|
-
|
12
|
-
class TessieVehicleFleet(VehicleFleet):
|
7
|
+
class TessieVehicle(VehicleFleet):
|
13
8
|
"""Tessie specific API vehicle."""
|
14
9
|
pass
|
15
10
|
|
16
11
|
class TessieVehicles(Vehicles):
|
17
12
|
"""Class containing and creating vehicles."""
|
18
13
|
|
19
|
-
|
20
|
-
|
21
|
-
def create(self, vin: str) -> TessieVehicleFleet:
|
22
|
-
"""Creates a specific vehicle."""
|
23
|
-
return self.createFleet(vin)
|
14
|
+
Vehicle = TessieVehicle
|
24
15
|
|
25
|
-
def
|
16
|
+
def create(self, vin: str) -> TessieVehicle:
|
26
17
|
"""Creates a specific vehicle."""
|
27
|
-
vehicle = self.
|
18
|
+
vehicle = self.Vehicle(self._parent, vin)
|
28
19
|
self[vin] = vehicle
|
29
20
|
return vehicle
|
30
21
|
|
22
|
+
def createFleet(self, vin: str):
|
23
|
+
"""Creates a specific vehicle."""
|
24
|
+
raise NotImplementedError("Tessie cannot use Fleet API directly")
|
25
|
+
|
31
26
|
def createSigned(self, vin: str):
|
32
27
|
"""Creates a specific vehicle."""
|
33
|
-
raise NotImplementedError("
|
28
|
+
raise NotImplementedError("Tessie cannot use Fleet API directly")
|
34
29
|
|
35
30
|
def createBluetooth(self, vin: str):
|
36
31
|
"""Creates a specific vehicle."""
|
37
|
-
raise NotImplementedError("
|
32
|
+
raise NotImplementedError("Tessie cannot use local Bluetooth")
|
@@ -0,0 +1,254 @@
|
|
1
|
+
Metadata-Version: 2.2
|
2
|
+
Name: tesla_fleet_api
|
3
|
+
Version: 1.0.13
|
4
|
+
Summary: Tesla Fleet API library for Python
|
5
|
+
Home-page: https://github.com/Teslemetry/python-tesla-fleet-api
|
6
|
+
Author: Brett Adams
|
7
|
+
Author-email: hello@teslemetry.com
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
11
|
+
Classifier: Operating System :: OS Independent
|
12
|
+
Requires-Python: >=3.10
|
13
|
+
Description-Content-Type: text/markdown
|
14
|
+
License-File: LICENSE
|
15
|
+
Requires-Dist: aiohttp
|
16
|
+
Requires-Dist: aiofiles
|
17
|
+
Requires-Dist: aiolimiter
|
18
|
+
Requires-Dist: cryptography
|
19
|
+
Requires-Dist: protobuf
|
20
|
+
Requires-Dist: bleak
|
21
|
+
Requires-Dist: bleak-retry-connector
|
22
|
+
Dynamic: author
|
23
|
+
Dynamic: author-email
|
24
|
+
Dynamic: classifier
|
25
|
+
Dynamic: description
|
26
|
+
Dynamic: description-content-type
|
27
|
+
Dynamic: home-page
|
28
|
+
Dynamic: requires-dist
|
29
|
+
Dynamic: requires-python
|
30
|
+
Dynamic: summary
|
31
|
+
|
32
|
+
# Tesla Fleet API
|
33
|
+
|
34
|
+
Tesla Fleet API is a Python library that provides an interface to interact with Tesla's Fleet API, including signed commands and encrypted local Bluetooth (BLE) communication. It also supports interactions with Teslemetry and Tessie services.
|
35
|
+
|
36
|
+
## Features
|
37
|
+
|
38
|
+
- Fleet API for vehicles
|
39
|
+
- Fleet API for energy sites
|
40
|
+
- Fleet API with signed vehicle commands
|
41
|
+
- Bluetooth for vehicles
|
42
|
+
- Teslemetry integration
|
43
|
+
- Tessie integration
|
44
|
+
|
45
|
+
## Installation
|
46
|
+
|
47
|
+
You can install the library using pip:
|
48
|
+
|
49
|
+
```bash
|
50
|
+
pip install tesla-fleet-api
|
51
|
+
```
|
52
|
+
|
53
|
+
## Usage
|
54
|
+
|
55
|
+
### Authentication
|
56
|
+
|
57
|
+
The `TeslaFleetOAuth` class provides methods that help with authenticating to the Tesla Fleet API. Here's a basic example:
|
58
|
+
|
59
|
+
```python
|
60
|
+
import asyncio
|
61
|
+
import aiohttp
|
62
|
+
from tesla_fleet_api import TeslaFleetOAuth
|
63
|
+
|
64
|
+
async def main():
|
65
|
+
async with aiohttp.ClientSession() as session:
|
66
|
+
oauth = TeslaFleetOAuth(
|
67
|
+
session=session,
|
68
|
+
client_id="<client_id>",
|
69
|
+
client_secret="<client_secret>",
|
70
|
+
redirect_uri="<redirect_uri>",
|
71
|
+
)
|
72
|
+
|
73
|
+
# Get the login URL and navigate the user to it
|
74
|
+
login_url = oauth.get_login_url(scopes=["openid", "email", "offline_access"])
|
75
|
+
print(f"Please go to {login_url} and authorize access.")
|
76
|
+
|
77
|
+
# After the user authorizes access, they will be redirected to the redirect_uri with a code
|
78
|
+
code = input("Enter the code you received: ")
|
79
|
+
|
80
|
+
# Exchange the code for a refresh token
|
81
|
+
await oauth.get_refresh_token(code)
|
82
|
+
print(f"Access token: {oauth.access_token}")
|
83
|
+
print(f"Refresh token: {oauth.refresh_token}")
|
84
|
+
# Dont forget to store the refresh token so you can use it again later
|
85
|
+
|
86
|
+
asyncio.run(main())
|
87
|
+
```
|
88
|
+
|
89
|
+
### Fleet API for Vehicles
|
90
|
+
|
91
|
+
The `TeslaFleetApi` class provides methods to interact with the Fleet API for vehicles. Here's a basic example:
|
92
|
+
|
93
|
+
```python
|
94
|
+
import asyncio
|
95
|
+
import aiohttp
|
96
|
+
from tesla_fleet_api import TeslaFleetApi
|
97
|
+
from tesla_fleet_api.exceptions import TeslaFleetError
|
98
|
+
|
99
|
+
async def main():
|
100
|
+
async with aiohttp.ClientSession() as session:
|
101
|
+
api = TeslaFleetApi(
|
102
|
+
access_token="<access_token>",
|
103
|
+
session=session,
|
104
|
+
region="na",
|
105
|
+
)
|
106
|
+
|
107
|
+
try:
|
108
|
+
data = await api.vehicles.list()
|
109
|
+
print(data)
|
110
|
+
except TeslaFleetError as e:
|
111
|
+
print(e)
|
112
|
+
|
113
|
+
asyncio.run(main())
|
114
|
+
```
|
115
|
+
|
116
|
+
For more detailed examples, see [Fleet API for Vehicles](docs/fleet_api_vehicles.md).
|
117
|
+
|
118
|
+
### Fleet API for Energy Sites
|
119
|
+
|
120
|
+
The `EnergySites` class provides methods to interact with the Fleet API for energy sites. Here's a basic example:
|
121
|
+
|
122
|
+
```python
|
123
|
+
import asyncio
|
124
|
+
import aiohttp
|
125
|
+
from tesla_fleet_api import TeslaFleetApi
|
126
|
+
from tesla_fleet_api.exceptions import TeslaFleetError
|
127
|
+
|
128
|
+
async def main():
|
129
|
+
async with aiohttp.ClientSession() as session:
|
130
|
+
api = TeslaFleetApi(
|
131
|
+
access_token="<access_token>",
|
132
|
+
session=session,
|
133
|
+
region="na",
|
134
|
+
)
|
135
|
+
|
136
|
+
try:
|
137
|
+
energy_sites = await api.energySites.list()
|
138
|
+
print(energy_sites)
|
139
|
+
except TeslaFleetError as e:
|
140
|
+
print(e)
|
141
|
+
|
142
|
+
asyncio.run(main())
|
143
|
+
```
|
144
|
+
|
145
|
+
For more detailed examples, see [Fleet API for Energy Sites](docs/fleet_api_energy_sites.md).
|
146
|
+
|
147
|
+
### Fleet API with Signed Vehicle Commands
|
148
|
+
|
149
|
+
The `VehicleSigned` class provides methods to interact with the Fleet API using signed vehicle commands. Here's a basic example:
|
150
|
+
|
151
|
+
```python
|
152
|
+
import asyncio
|
153
|
+
import aiohttp
|
154
|
+
from tesla_fleet_api import TeslaFleetApi
|
155
|
+
from tesla_fleet_api.tesla.vehicle.signed import VehicleSigned
|
156
|
+
from tesla_fleet_api.exceptions import TeslaFleetError
|
157
|
+
|
158
|
+
async def main():
|
159
|
+
async with aiohttp.ClientSession() as session:
|
160
|
+
api = TeslaFleetApi(
|
161
|
+
access_token="<access_token>",
|
162
|
+
session=session,
|
163
|
+
region="na",
|
164
|
+
)
|
165
|
+
|
166
|
+
try:
|
167
|
+
vehicle = VehicleSigned(api, "<vin>")
|
168
|
+
await vehicle.handshake()
|
169
|
+
data = await vehicle.wake_up()
|
170
|
+
print(data)
|
171
|
+
except TeslaFleetError as e:
|
172
|
+
print(e)
|
173
|
+
|
174
|
+
asyncio.run(main())
|
175
|
+
```
|
176
|
+
|
177
|
+
For more detailed examples, see [Fleet API with Signed Vehicle Commands](docs/fleet_api_signed_commands.md).
|
178
|
+
|
179
|
+
### Bluetooth for Vehicles
|
180
|
+
|
181
|
+
The `TeslaBluetooth` class provides methods to interact with Tesla vehicles using Bluetooth. Here's a basic example:
|
182
|
+
|
183
|
+
```python
|
184
|
+
import asyncio
|
185
|
+
from bleak import BleakScanner
|
186
|
+
from tesla_fleet_api import TeslaBluetooth
|
187
|
+
|
188
|
+
async def main():
|
189
|
+
scanner = BleakScanner()
|
190
|
+
devices = await scanner.discover()
|
191
|
+
for device in devices:
|
192
|
+
if TeslaBluetooth().valid_name(device.name):
|
193
|
+
print(f"Found Tesla vehicle: {device.name}")
|
194
|
+
|
195
|
+
asyncio.run(main())
|
196
|
+
```
|
197
|
+
|
198
|
+
For more detailed examples, see [Bluetooth for Vehicles](docs/bluetooth_vehicles.md).
|
199
|
+
|
200
|
+
### Teslemetry
|
201
|
+
|
202
|
+
The `Teslemetry` class provides methods to interact with the Teslemetry service. Here's a basic example:
|
203
|
+
|
204
|
+
```python
|
205
|
+
import asyncio
|
206
|
+
import aiohttp
|
207
|
+
from tesla_fleet_api import Teslemetry
|
208
|
+
from tesla_fleet_api.exceptions import TeslaFleetError
|
209
|
+
|
210
|
+
async def main():
|
211
|
+
async with aiohttp.ClientSession() as session:
|
212
|
+
api = Teslemetry(
|
213
|
+
access_token="<access_token>",
|
214
|
+
session=session,
|
215
|
+
)
|
216
|
+
|
217
|
+
try:
|
218
|
+
data = await api.vehicles.list()
|
219
|
+
print(data)
|
220
|
+
except TeslaFleetError as e:
|
221
|
+
print(e)
|
222
|
+
|
223
|
+
asyncio.run(main())
|
224
|
+
```
|
225
|
+
|
226
|
+
For more detailed examples, see [Teslemetry](docs/teslemetry.md).
|
227
|
+
|
228
|
+
### Tessie
|
229
|
+
|
230
|
+
The `Tessie` class provides methods to interact with the Tessie service. Here's a basic example:
|
231
|
+
|
232
|
+
```python
|
233
|
+
import asyncio
|
234
|
+
import aiohttp
|
235
|
+
from tesla_fleet_api import Tessie
|
236
|
+
from tesla_fleet_api.exceptions import TeslaFleetError
|
237
|
+
|
238
|
+
async def main():
|
239
|
+
async with aiohttp.ClientSession() as session:
|
240
|
+
api = Tessie(
|
241
|
+
access_token="<access_token>",
|
242
|
+
session=session,
|
243
|
+
)
|
244
|
+
|
245
|
+
try:
|
246
|
+
data = await api.vehicles.list()
|
247
|
+
print(data)
|
248
|
+
except TeslaFleetError as e:
|
249
|
+
print(e)
|
250
|
+
|
251
|
+
asyncio.run(main())
|
252
|
+
```
|
253
|
+
|
254
|
+
For more detailed examples, see [Tessie](docs/tessie.md).
|
@@ -1,23 +1,23 @@
|
|
1
1
|
tesla_fleet_api/__init__.py,sha256=3DZMoZ-5srW-7SooAjqcRubQDuZPY8rMKH7eqIp4qtg,392
|
2
|
-
tesla_fleet_api/const.py,sha256=
|
2
|
+
tesla_fleet_api/const.py,sha256=5-oJdYgUuRnEr7ZwnJGoYXWLViNVxfFPmkYHzC5SAYs,3900
|
3
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
|
-
tesla_fleet_api/tesla/__init__.py,sha256=
|
5
|
+
tesla_fleet_api/tesla/__init__.py,sha256=1XjIJWgHsYHLw9qgiVRh4FrHVOH9V8tGF0hE92PQhX8,769
|
6
6
|
tesla_fleet_api/tesla/bluetooth.py,sha256=lyPRVf1YdcElrYBsKOMCaLwMPE9rO7Iw1a6nE7VUZ94,2369
|
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=
|
9
|
+
tesla_fleet_api/tesla/fleet.py,sha256=L82pVz7GJ8wKkUGqvvYnx9Kc13Bpu9LhDtmt4CV7H0g,5388
|
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
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
|
-
tesla_fleet_api/tesla/vehicle/__init__.py,sha256=
|
15
|
-
tesla_fleet_api/tesla/vehicle/bluetooth.py,sha256=
|
16
|
-
tesla_fleet_api/tesla/vehicle/commands.py,sha256=
|
14
|
+
tesla_fleet_api/tesla/vehicle/__init__.py,sha256=EYqPyPjaD9JP846c1iGBkk697VZ-eY_tcHntDixhDGE,425
|
15
|
+
tesla_fleet_api/tesla/vehicle/bluetooth.py,sha256=rMxKOrc8T3Wz_sbuGYFot4ffCbOVP2jFSCd5tVuSWv0,21035
|
16
|
+
tesla_fleet_api/tesla/vehicle/commands.py,sha256=L3apfGwry-IBuSSLbuoPPjW_q4new2xTr6Iy_kUa2pA,51268
|
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=IBvRO6qGkUf4bi-pFongA9fIe9iScvz_wGXtzEJXilY,870
|
20
|
-
tesla_fleet_api/tesla/vehicle/vehicles.py,sha256=
|
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
|
@@ -38,14 +38,14 @@ tesla_fleet_api/tesla/vehicle/proto/vcsec_pb2.py,sha256=PDv9TfiXnNs6sQ0D5vBrsSSP
|
|
38
38
|
tesla_fleet_api/tesla/vehicle/proto/vcsec_pb2.pyi,sha256=cyK1uyRtDjRVqVlyl5uRQYY1RhFlWSJheLg3PGfs-_s,28524
|
39
39
|
tesla_fleet_api/tesla/vehicle/proto/vehicle_pb2.py,sha256=bqyFJM-1qZ7W9XKREINhYZx8yXAudmq6W8_Pdfkhbkk,44711
|
40
40
|
tesla_fleet_api/tesla/vehicle/proto/vehicle_pb2.pyi,sha256=sAUW_9aVB8NqJCnhZjXMLfqfePLVZv_7PfSKZKEBaQA,74251
|
41
|
-
tesla_fleet_api/teslemetry/__init__.py,sha256=
|
41
|
+
tesla_fleet_api/teslemetry/__init__.py,sha256=Vh7mh2r0L-7Oh-tIs5sAtHmE3OWV7UtyIbeazlTTHcc,434
|
42
42
|
tesla_fleet_api/teslemetry/teslemetry.py,sha256=wcZOD1GnZ4lwC9LoicuSNbZ3z-ka9I4m1AMZP4LDYCY,3088
|
43
|
-
tesla_fleet_api/teslemetry/vehicles.py,sha256=
|
44
|
-
tesla_fleet_api/tessie/__init__.py,sha256=
|
43
|
+
tesla_fleet_api/teslemetry/vehicles.py,sha256=IJMRKb3jrW9ZSdJrCQ5wOYYZGVbFvSVza24Zpdh4d_0,2067
|
44
|
+
tesla_fleet_api/tessie/__init__.py,sha256=WCH4z4WCjYEcSqQRwpWydZ6yulWA6422l4J55XfPmPg,406
|
45
45
|
tesla_fleet_api/tessie/tessie.py,sha256=uhg0oOIxpwDvlvdBhKHeF3AGR2PzmdBgzh2-_EkmSq0,2617
|
46
|
-
tesla_fleet_api/tessie/vehicles.py,sha256=
|
47
|
-
tesla_fleet_api-1.0.
|
48
|
-
tesla_fleet_api-1.0.
|
49
|
-
tesla_fleet_api-1.0.
|
50
|
-
tesla_fleet_api-1.0.
|
51
|
-
tesla_fleet_api-1.0.
|
46
|
+
tesla_fleet_api/tessie/vehicles.py,sha256=Cdp0pt2Z8bzK5T9wQ8_KubGSZ3Gevx1Q18kqJqEUxfs,1011
|
47
|
+
tesla_fleet_api-1.0.13.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
48
|
+
tesla_fleet_api-1.0.13.dist-info/METADATA,sha256=x8_qvQ6DGF46nxDlnUTnRpekd6j65bT_rSCmfXPN_fo,6960
|
49
|
+
tesla_fleet_api-1.0.13.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
|
50
|
+
tesla_fleet_api-1.0.13.dist-info/top_level.txt,sha256=jeNbog_1saXBFrGpom9WyPWmilxsyP3szL_G7JLWQfM,16
|
51
|
+
tesla_fleet_api-1.0.13.dist-info/RECORD,,
|
@@ -1,158 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.2
|
2
|
-
Name: tesla_fleet_api
|
3
|
-
Version: 1.0.11
|
4
|
-
Summary: Tesla Fleet API library for Python
|
5
|
-
Home-page: https://github.com/Teslemetry/python-tesla-fleet-api
|
6
|
-
Author: Brett Adams
|
7
|
-
Author-email: hello@teslemetry.com
|
8
|
-
Classifier: Development Status :: 5 - Production/Stable
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
10
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
11
|
-
Classifier: Operating System :: OS Independent
|
12
|
-
Requires-Python: >=3.10
|
13
|
-
Description-Content-Type: text/markdown
|
14
|
-
License-File: LICENSE
|
15
|
-
Requires-Dist: aiohttp
|
16
|
-
Requires-Dist: aiofiles
|
17
|
-
Requires-Dist: aiolimiter
|
18
|
-
Requires-Dist: cryptography
|
19
|
-
Requires-Dist: protobuf
|
20
|
-
Requires-Dist: bleak
|
21
|
-
Requires-Dist: bleak-retry-connector
|
22
|
-
Dynamic: author
|
23
|
-
Dynamic: author-email
|
24
|
-
Dynamic: classifier
|
25
|
-
Dynamic: description
|
26
|
-
Dynamic: description-content-type
|
27
|
-
Dynamic: home-page
|
28
|
-
Dynamic: requires-dist
|
29
|
-
Dynamic: requires-python
|
30
|
-
Dynamic: summary
|
31
|
-
|
32
|
-
# Tesla Fleet Api
|
33
|
-
Python library for Tesla Fleet API and Tesla Command Protocol, including signed commands and encrypted local Bluetooth (BLE). Also provides interfaces for Teslemetry and Tessie.
|
34
|
-
|
35
|
-
Based on [Tesla Developer documentation](https://developer.tesla.com/docs/fleet-api) and [Tesla Command Protocol](https://github.com/teslamotors/vehicle-command/blob/main/pkg/protocol/protocol.md)
|
36
|
-
|
37
|
-
**Documentation is currently outdated for V1.0.X**
|
38
|
-
|
39
|
-
## TeslaFleetApi
|
40
|
-
This is the base class, however can also be used directly if you have a valid user access_token.
|
41
|
-
|
42
|
-
```
|
43
|
-
import asyncio
|
44
|
-
import aiohttp
|
45
|
-
|
46
|
-
from tesla_fleet_api import TeslaFleetApi
|
47
|
-
from tesla_fleet_api.exceptions import TeslaFleetError
|
48
|
-
|
49
|
-
|
50
|
-
async def main():
|
51
|
-
async with aiohttp.ClientSession() as session:
|
52
|
-
api = TeslaFleetApi(
|
53
|
-
access_token="<access_token>",
|
54
|
-
session=session,
|
55
|
-
region="na",
|
56
|
-
)
|
57
|
-
|
58
|
-
try:
|
59
|
-
data = await api.vehicle.list()
|
60
|
-
print(data)
|
61
|
-
except TeslaFleetError as e:
|
62
|
-
print(e)
|
63
|
-
|
64
|
-
asyncio.run(main())
|
65
|
-
```
|
66
|
-
|
67
|
-
## TeslaFleetOAuth
|
68
|
-
This extends TeslaFleetApi to support OAuth, and requires a client_id, and either a refresh_token or initial authentication code.
|
69
|
-
|
70
|
-
```
|
71
|
-
from tesla_fleet_api import TeslaFleetOAuth
|
72
|
-
from tesla_fleet_api.exceptions import TeslaFleetError
|
73
|
-
import json
|
74
|
-
|
75
|
-
async def main():
|
76
|
-
with open("auth.json", "r") as f:
|
77
|
-
auth = json.load(f)
|
78
|
-
async with aiohttp.ClientSession() as session:
|
79
|
-
api = TeslaFleetOAuth(
|
80
|
-
session,
|
81
|
-
client_id=<client_id>,
|
82
|
-
access_token=auth["access_token"],
|
83
|
-
refresh_token=auth["refresh_token"],
|
84
|
-
expires=auth["expires"],
|
85
|
-
region="na",
|
86
|
-
)
|
87
|
-
try:
|
88
|
-
data = await api.vehicle.list()
|
89
|
-
print(data)
|
90
|
-
except TeslaFleetError as e:
|
91
|
-
print(e)
|
92
|
-
|
93
|
-
with open("auth.json", "w") as f:
|
94
|
-
json.dump(
|
95
|
-
{
|
96
|
-
"access_token": api.access_token,
|
97
|
-
"refresh_token": api.refresh_token,
|
98
|
-
"expires": api.expires,
|
99
|
-
},
|
100
|
-
f,
|
101
|
-
)
|
102
|
-
|
103
|
-
asyncio.run(main())
|
104
|
-
```
|
105
|
-
|
106
|
-
## Teslemetry
|
107
|
-
This extends TeslaFleetApi to send requests through Teslemetry, which manages all aspects of Tesla OAuth. This class only requires an access_token from the Teslemetry console.
|
108
|
-
|
109
|
-
```
|
110
|
-
import asyncio
|
111
|
-
import aiohttp
|
112
|
-
|
113
|
-
from tesla_fleet_api import Teslemetry
|
114
|
-
from tesla_fleet_api.exceptions import TeslaFleetError
|
115
|
-
|
116
|
-
|
117
|
-
async def main():
|
118
|
-
async with aiohttp.ClientSession() as session:
|
119
|
-
api = Teslemetry(
|
120
|
-
access_token="<access_token>",
|
121
|
-
session=session,
|
122
|
-
)
|
123
|
-
|
124
|
-
try:
|
125
|
-
data = await api.vehicle.list()
|
126
|
-
print(data)
|
127
|
-
except TeslaFleetError as e:
|
128
|
-
print(e)
|
129
|
-
|
130
|
-
asyncio.run(main())
|
131
|
-
```
|
132
|
-
|
133
|
-
## Tessie
|
134
|
-
This extends TeslaFleetApi to send requests through Tessie, which manages all aspects of Tesla OAuth. This class only requires an access_token from [Tessie](https://dash.tessie.com/settings/api).
|
135
|
-
|
136
|
-
```
|
137
|
-
import asyncio
|
138
|
-
import aiohttp
|
139
|
-
|
140
|
-
from tesla_fleet_api import Tessie
|
141
|
-
from tesla_fleet_api.exceptions import TeslaFleetError
|
142
|
-
|
143
|
-
|
144
|
-
async def main():
|
145
|
-
async with aiohttp.ClientSession() as session:
|
146
|
-
api = Tessie(
|
147
|
-
access_token="<access_token>",
|
148
|
-
session=session,
|
149
|
-
)
|
150
|
-
|
151
|
-
try:
|
152
|
-
data = await api.vehicle.list()
|
153
|
-
print(data)
|
154
|
-
except TeslaFleetError as e:
|
155
|
-
print(e)
|
156
|
-
|
157
|
-
asyncio.run(main())
|
158
|
-
```
|
File without changes
|
File without changes
|