tesla-fleet-api 0.0.1__py3-none-any.whl → 0.0.4__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/TeslaFleetApi.py +715 -271
- tesla_fleet_api/TeslaFleetOAuth.py +50 -4
- tesla_fleet_api/Teslemetry.py +25 -0
- tesla_fleet_api/__init__.py +0 -1
- tesla_fleet_api/const.py +236 -2
- tesla_fleet_api/exceptions.py +180 -122
- {tesla_fleet_api-0.0.1.dist-info → tesla_fleet_api-0.0.4.dist-info}/METADATA +37 -3
- tesla_fleet_api-0.0.4.dist-info/RECORD +11 -0
- tesla_fleet_api-0.0.1.dist-info/RECORD +0 -11
- {tesla_fleet_api-0.0.1.dist-info → tesla_fleet_api-0.0.4.dist-info}/LICENSE +0 -0
- {tesla_fleet_api-0.0.1.dist-info → tesla_fleet_api-0.0.4.dist-info}/WHEEL +0 -0
- {tesla_fleet_api-0.0.1.dist-info → tesla_fleet_api-0.0.4.dist-info}/top_level.txt +0 -0
tesla_fleet_api/exceptions.py
CHANGED
@@ -1,218 +1,276 @@
|
|
1
1
|
import aiohttp
|
2
|
-
import json
|
3
2
|
from .const import Errors
|
4
3
|
|
5
4
|
|
6
|
-
class TeslaFleetError:
|
5
|
+
class TeslaFleetError(BaseException):
|
7
6
|
"""Base class for all Tesla exceptions."""
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
error_description: str | None
|
8
|
+
message: str
|
9
|
+
status: int
|
10
|
+
error: str | None
|
11
|
+
error_description: str | None
|
14
12
|
|
15
|
-
|
16
|
-
|
13
|
+
def __init__(self, data: dict[str, str] | None = None):
|
14
|
+
if data:
|
15
|
+
self.status = data.get("status", self.status)
|
17
16
|
self.error = data.get("error") or data.get("message")
|
18
17
|
self.error_description = data.get("error_description")
|
19
18
|
|
20
|
-
class InvalidCommand(Base):
|
21
|
-
"""The data request or command is unknown."""
|
22
19
|
|
23
|
-
|
20
|
+
class InvalidCommand(TeslaFleetError):
|
21
|
+
"""The data request or command is unknown."""
|
24
22
|
|
25
|
-
|
26
|
-
|
23
|
+
message = "The data request or command is unknown."
|
24
|
+
status = 400
|
27
25
|
|
28
|
-
message = "A field in the input is not valid."
|
29
26
|
|
30
|
-
|
31
|
-
|
27
|
+
class InvalidField(TeslaFleetError):
|
28
|
+
"""A field in the input is not valid."""
|
32
29
|
|
33
|
-
|
30
|
+
message = "A field in the input is not valid."
|
31
|
+
status = 400
|
34
32
|
|
35
|
-
class InvalidAuthCode(Base):
|
36
|
-
"""The "code" in request body is invalid, generate a new one and try again."""
|
37
33
|
|
38
|
-
|
39
|
-
|
40
|
-
)
|
34
|
+
class InvalidRequest(TeslaFleetError):
|
35
|
+
"""The request body is not valid, a description giving a more specific error message may be returned."""
|
41
36
|
|
42
|
-
|
43
|
-
|
37
|
+
message = "The request body is not valid"
|
38
|
+
status = 400
|
44
39
|
|
45
|
-
message = "Invalid redirect URI/URL. The authorize redirect URI and token redirect URI must match."
|
46
40
|
|
47
|
-
|
48
|
-
|
41
|
+
class InvalidAuthCode(TeslaFleetError):
|
42
|
+
"""The "code" in request body is invalid, generate a new one and try again."""
|
49
43
|
|
50
|
-
|
44
|
+
message = "The 'code' in request body is invalid, generate a new one and try again."
|
45
|
+
status = 400
|
51
46
|
|
52
|
-
class MobileAccessDisabled(Base):
|
53
|
-
"""The vehicle has turned off remote access."""
|
54
47
|
|
55
|
-
|
48
|
+
class InvalidRedirectUrl(TeslaFleetError):
|
49
|
+
"""Invalid redirect URI/URL. The authorize redirect URI and token redirect URI must match."""
|
56
50
|
|
57
|
-
|
58
|
-
|
51
|
+
message = "Invalid redirect URI/URL. The authorize redirect URI and token redirect URI must match."
|
52
|
+
status = 400
|
59
53
|
|
60
|
-
message = "The OAuth token has expired."
|
61
54
|
|
62
|
-
|
63
|
-
|
55
|
+
class UnauthorizedClient(TeslaFleetError):
|
56
|
+
"""We don't recognize this client_id and client_secret combination. Use the client_id and client_secret that has been granted for the application."""
|
64
57
|
|
65
|
-
|
58
|
+
message = "We don't recognize this client_id and client_secret combination. Use the client_id and client_secret that has been granted for the application."
|
59
|
+
status = 400
|
66
60
|
|
67
|
-
class Forbidden(Base):
|
68
|
-
"""Access to this resource is not authorized, developers should check required scopes."""
|
69
61
|
|
70
|
-
|
62
|
+
class MobileAccessDisabled(TeslaFleetError):
|
63
|
+
"""The vehicle has turned off remote access."""
|
71
64
|
|
72
|
-
|
73
|
-
|
65
|
+
message = "The vehicle has turned off remote access."
|
66
|
+
status = 401
|
74
67
|
|
75
|
-
message = "The requested resource does not exist."
|
76
68
|
|
77
|
-
|
78
|
-
|
69
|
+
class OAuthExpired(TeslaFleetError):
|
70
|
+
"""The OAuth token has expired."""
|
79
71
|
|
80
|
-
|
72
|
+
message = "The OAuth token has expired."
|
73
|
+
status = 401
|
81
74
|
|
82
|
-
class NotAcceptable(Base):
|
83
|
-
"""The HTTP request does not have a Content-Type header set to application/json."""
|
84
75
|
|
85
|
-
|
76
|
+
class PaymentRequired(TeslaFleetError):
|
77
|
+
"""Payment is required in order to use the API (non-free account only)."""
|
86
78
|
|
87
|
-
|
88
|
-
|
79
|
+
message = "Payment is required in order to use the API (non-free account only)."
|
80
|
+
status = 402
|
89
81
|
|
90
|
-
message = "The vehicle is not 'online'."
|
91
82
|
|
92
|
-
|
93
|
-
|
83
|
+
class Forbidden(TeslaFleetError):
|
84
|
+
"""Access to this resource is not authorized, developers should check required scopes."""
|
94
85
|
|
95
|
-
|
86
|
+
message = "Access to this resource is not authorized, developers should check required scopes."
|
87
|
+
status = 403
|
96
88
|
|
97
|
-
class InvalidRegion(Base):
|
98
|
-
"""This user is not present in the current region."""
|
99
89
|
|
100
|
-
|
90
|
+
class NotFound(TeslaFleetError):
|
91
|
+
"""The requested resource does not exist."""
|
101
92
|
|
102
|
-
|
103
|
-
|
93
|
+
message = "The requested resource does not exist."
|
94
|
+
status = 404
|
104
95
|
|
105
|
-
message = (
|
106
|
-
"There is a semantic problem with the data, e.g. missing or invalid data."
|
107
|
-
)
|
108
96
|
|
109
|
-
|
110
|
-
|
97
|
+
class NotAllowed(TeslaFleetError):
|
98
|
+
"""The operation is not allowed."""
|
111
99
|
|
112
|
-
|
100
|
+
message = "The operation is not allowed."
|
101
|
+
status = 405
|
113
102
|
|
114
|
-
class RateLimited(Base):
|
115
|
-
"""Account or server is rate limited."""
|
116
103
|
|
117
|
-
|
104
|
+
class NotAcceptable(TeslaFleetError):
|
105
|
+
"""The HTTP request does not have a Content-Type header set to application/json."""
|
118
106
|
|
119
|
-
|
120
|
-
"
|
107
|
+
message = (
|
108
|
+
"The HTTP request does not have a Content-Type header set to application/json."
|
109
|
+
)
|
110
|
+
status = 406
|
121
111
|
|
122
|
-
message = "Querying for a user/vehicle without proper privacy settings."
|
123
112
|
|
124
|
-
|
125
|
-
|
113
|
+
class VehicleOffline(TeslaFleetError):
|
114
|
+
"""The vehicle is not "online."""
|
126
115
|
|
127
|
-
|
128
|
-
|
129
|
-
)
|
116
|
+
message = "The vehicle is not 'online'."
|
117
|
+
status = 408
|
130
118
|
|
131
|
-
class InternalServerError(Base):
|
132
|
-
"""An error occurred while processing the request."""
|
133
119
|
|
134
|
-
|
120
|
+
class PreconditionFailed(TeslaFleetError):
|
121
|
+
"""A condition has not been met to process the request."""
|
135
122
|
|
136
|
-
|
137
|
-
|
123
|
+
message = "A condition has not been met to process the request."
|
124
|
+
status = 412
|
138
125
|
|
139
|
-
message = "Either an internal service or a vehicle did not respond (timeout)."
|
140
126
|
|
141
|
-
|
142
|
-
|
127
|
+
class InvalidRegion(TeslaFleetError):
|
128
|
+
"""This user is not present in the current region."""
|
143
129
|
|
144
|
-
|
130
|
+
message = "This user is not present in the current region."
|
131
|
+
status = 421
|
145
132
|
|
146
|
-
class DeviceUnexpectedResponse(Base):
|
147
|
-
"""Vehicle responded with an error - might need a reboot, OTA update, or service."""
|
148
133
|
|
149
|
-
|
134
|
+
class InvalidResource(TeslaFleetError):
|
135
|
+
"""There is a semantic problem with the data, e.g. missing or invalid data."""
|
136
|
+
|
137
|
+
message = "There is a semantic problem with the data, e.g. missing or invalid data."
|
138
|
+
status = 422
|
139
|
+
|
140
|
+
|
141
|
+
class Locked(TeslaFleetError):
|
142
|
+
"""Account is locked, and must be unlocked by Tesla."""
|
143
|
+
|
144
|
+
message = "Account is locked, and must be unlocked by Tesla."
|
145
|
+
status = 423
|
146
|
+
|
147
|
+
|
148
|
+
class RateLimited(TeslaFleetError):
|
149
|
+
"""Account or server is rate limited."""
|
150
|
+
|
151
|
+
message = "Account or server is rate limited."
|
152
|
+
status = 429
|
153
|
+
|
154
|
+
|
155
|
+
class ResourceUnavailableForLegalReasons(TeslaFleetError):
|
156
|
+
"""Querying for a user/vehicle without proper privacy settings."""
|
157
|
+
|
158
|
+
message = "Querying for a user/vehicle without proper privacy settings."
|
159
|
+
status = 451
|
160
|
+
|
161
|
+
|
162
|
+
class ClientClosedRequest(TeslaFleetError):
|
163
|
+
"""Client has closed the request before the server could send a response."""
|
164
|
+
|
165
|
+
message = "Client has closed the request before the server could send a response."
|
166
|
+
status = 499
|
167
|
+
|
168
|
+
|
169
|
+
class InternalServerError(TeslaFleetError):
|
170
|
+
"""An error occurred while processing the request."""
|
171
|
+
|
172
|
+
message = "An error occurred while processing the request."
|
173
|
+
status = 500
|
174
|
+
|
175
|
+
|
176
|
+
class ServiceUnavailable(TeslaFleetError):
|
177
|
+
"""Either an internal service or a vehicle did not respond (timeout)."""
|
178
|
+
|
179
|
+
message = "Either an internal service or a vehicle did not respond (timeout)."
|
180
|
+
status = 503
|
181
|
+
|
182
|
+
|
183
|
+
class GatewayTimeout(TeslaFleetError):
|
184
|
+
"""Server did not receive a response."""
|
185
|
+
|
186
|
+
message = "Server did not receive a response."
|
187
|
+
status = 504
|
188
|
+
|
189
|
+
|
190
|
+
class DeviceUnexpectedResponse(TeslaFleetError):
|
191
|
+
"""Vehicle responded with an error - might need a reboot, OTA update, or service."""
|
192
|
+
|
193
|
+
message = (
|
194
|
+
"Vehicle responded with an error - might need a reboot, OTA update, or service."
|
195
|
+
)
|
196
|
+
status = 540
|
197
|
+
|
198
|
+
|
199
|
+
class InvalidToken(TeslaFleetError): # Teslemetry specific
|
200
|
+
"""Teslemetry specific error for invalid access token."""
|
201
|
+
|
202
|
+
message = "Invalid Teslemetry access token."
|
203
|
+
status = 401
|
204
|
+
|
205
|
+
|
206
|
+
class LibraryError(Exception):
|
207
|
+
"""Errors related to this library."""
|
150
208
|
|
151
209
|
|
152
210
|
async def raise_for_status(resp: aiohttp.ClientResponse) -> None:
|
153
211
|
"""Raise an exception if the response status code is >=400."""
|
154
212
|
# https://developer.tesla.com/docs/fleet-api#response-codes
|
155
213
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
214
|
+
if e.status == 401 and resp.content_length == 0:
|
215
|
+
# This error does not return a body
|
216
|
+
raise OAuthExpired() from e
|
217
|
+
|
218
|
+
data = await resp.json()
|
219
|
+
|
160
220
|
try:
|
161
221
|
resp.raise_for_status()
|
162
222
|
except aiohttp.ClientResponseError as e:
|
163
223
|
if e.status == 400:
|
164
224
|
if data.error == Errors.INVALID_COMMAND:
|
165
|
-
raise
|
225
|
+
raise InvalidCommand(data) from e
|
166
226
|
elif data.error == Errors.INVALID_FIELD:
|
167
|
-
raise
|
227
|
+
raise InvalidField(data) from e
|
168
228
|
elif data.error == Errors.INVALID_REQUEST:
|
169
|
-
raise
|
229
|
+
raise InvalidRequest(data) from e
|
170
230
|
elif data.error == Errors.INVALID_AUTH_CODE:
|
171
|
-
raise
|
231
|
+
raise InvalidAuthCode(data) from e
|
172
232
|
elif data.error == Errors.INVALID_REDIRECT_URL:
|
173
|
-
raise
|
233
|
+
raise InvalidRedirectUrl(data) from e
|
174
234
|
elif data.error == Errors.UNAUTHORIZED_CLIENT:
|
175
|
-
raise
|
235
|
+
raise UnauthorizedClient(data) from e
|
176
236
|
elif e.status == 401:
|
177
237
|
if data.error == Errors.MOBILE_ACCESS_DISABLED:
|
178
|
-
raise
|
179
|
-
elif data.error == Errors.
|
180
|
-
raise
|
238
|
+
raise MobileAccessDisabled(data) from e
|
239
|
+
elif data.error == Errors.INVALID_TOKEN:
|
240
|
+
raise InvalidToken(data) from e
|
181
241
|
elif e.status == 402:
|
182
|
-
raise
|
242
|
+
raise PaymentRequired(data) from e
|
183
243
|
elif e.status == 403:
|
184
|
-
raise
|
244
|
+
raise Forbidden(data) from e
|
185
245
|
elif e.status == 404:
|
186
|
-
raise
|
246
|
+
raise NotFound(data) from e
|
187
247
|
elif e.status == 405:
|
188
|
-
raise
|
248
|
+
raise NotAllowed(data) from e
|
189
249
|
elif e.status == 406:
|
190
|
-
raise
|
250
|
+
raise NotAcceptable(data) from e
|
191
251
|
elif e.status == 408:
|
192
|
-
raise
|
252
|
+
raise VehicleOffline(data) from e
|
193
253
|
elif e.status == 412:
|
194
|
-
raise
|
254
|
+
raise PreconditionFailed(data) from e
|
195
255
|
elif e.status == 421:
|
196
|
-
raise
|
256
|
+
raise InvalidRegion(data) from e
|
197
257
|
elif e.status == 422:
|
198
|
-
raise
|
258
|
+
raise InvalidResource(data) from e
|
199
259
|
elif e.status == 423:
|
200
|
-
raise
|
260
|
+
raise Locked(data) from e
|
201
261
|
elif e.status == 429:
|
202
|
-
raise
|
262
|
+
raise RateLimited(data) from e
|
203
263
|
elif e.status == 451:
|
204
|
-
raise
|
205
|
-
e.status, data
|
206
|
-
) from e
|
264
|
+
raise ResourceUnavailableForLegalReasons(data) from e
|
207
265
|
elif e.status == 499:
|
208
|
-
raise
|
266
|
+
raise ClientClosedRequest(data) from e
|
209
267
|
elif e.status == 500:
|
210
|
-
raise
|
268
|
+
raise InternalServerError(data) from e
|
211
269
|
elif e.status == 503:
|
212
|
-
raise
|
270
|
+
raise ServiceUnavailable(data) from e
|
213
271
|
elif e.status == 504:
|
214
|
-
raise
|
272
|
+
raise GatewayTimeout(data) from e
|
215
273
|
elif e.status == 540:
|
216
|
-
raise
|
274
|
+
raise DeviceUnexpectedResponse(data) from e
|
217
275
|
else:
|
218
276
|
raise e
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: tesla_fleet_api
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.4
|
4
4
|
Summary: Tesla Fleet API library for Python
|
5
5
|
Home-page: https://github.com/Teslemetry/tesla_fleet_api
|
6
6
|
Author: Brett Adams
|
@@ -15,12 +15,16 @@ License-File: LICENSE
|
|
15
15
|
Requires-Dist: aiohttp
|
16
16
|
|
17
17
|
# tesla_fleet_api
|
18
|
-
Python library for Tesla Fleet API and Teslemetry
|
18
|
+
Python library for Tesla Fleet API and Teslemetry.
|
19
19
|
|
20
|
-
Currently does not support the
|
20
|
+
Currently does not support the end to end encrypted telemetry or command API.
|
21
21
|
|
22
22
|
Based on [Tesla Developer documentation](https://developer.tesla.com/docs/fleet-api).
|
23
23
|
|
24
|
+
|
25
|
+
## TeslaFleetApi
|
26
|
+
This is the base class, however can also be used directly if you have a valid user access_token.
|
27
|
+
|
24
28
|
```
|
25
29
|
import asyncio
|
26
30
|
import aiohttp
|
@@ -45,3 +49,33 @@ async def main():
|
|
45
49
|
|
46
50
|
asyncio.run(main())
|
47
51
|
```
|
52
|
+
|
53
|
+
## TeslaFleetOAuth
|
54
|
+
This extends TeslaFleetApi to support OAuth, and requires a client_id, and either a refresh_token or initial authentication code.
|
55
|
+
|
56
|
+
## Teslemetry
|
57
|
+
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.
|
58
|
+
|
59
|
+
```
|
60
|
+
import asyncio
|
61
|
+
import aiohttp
|
62
|
+
|
63
|
+
from tesla_fleet_api import Teslemetry, TeslaFleetError
|
64
|
+
|
65
|
+
|
66
|
+
async def main():
|
67
|
+
async with aiohttp.ClientSession() as session:
|
68
|
+
api = Teslemetry(
|
69
|
+
access_token="<access_token>",
|
70
|
+
session=session,
|
71
|
+
raise_for_status=True,
|
72
|
+
)
|
73
|
+
|
74
|
+
try:
|
75
|
+
data = await api.vehicle.list()
|
76
|
+
print(data)
|
77
|
+
except TeslaFleetError.Base as e:
|
78
|
+
print(e.message, e.error)
|
79
|
+
|
80
|
+
asyncio.run(main())
|
81
|
+
```
|
@@ -0,0 +1,11 @@
|
|
1
|
+
tesla_fleet_api/TeslaFleetApi.py,sha256=khQJxemvcrPSjIdt1_SCpaQEbdHB-dBcABkOUFcvtJ4,68779
|
2
|
+
tesla_fleet_api/TeslaFleetOAuth.py,sha256=ra14xPhlaUc71T9fiEH3g8P2PzUnVHSG9cMe8jMhil8,1982
|
3
|
+
tesla_fleet_api/Teslemetry.py,sha256=izjIwzLNbsl9INc5LdtoIVvCbBolmScLTGSttzU1_CA,1491
|
4
|
+
tesla_fleet_api/__init__.py,sha256=HvYW6aikklbgaa6n0gB49ZXcWV0c7tyqW-fvIk86qtE,121
|
5
|
+
tesla_fleet_api/const.py,sha256=pJMdikCcERfdSt6nh6KiDcV5op1XfyaOv7qKAWE7fO4,8592
|
6
|
+
tesla_fleet_api/exceptions.py,sha256=uAfITaX4D2K1PgozTHOStjZfcksmNvxKf5YRLWkiZDU,8588
|
7
|
+
tesla_fleet_api-0.0.4.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
8
|
+
tesla_fleet_api-0.0.4.dist-info/METADATA,sha256=X4C43gQsM8DGZ_h6J_n4tVPjyEcIcgDEYYF4YV7vP10,2235
|
9
|
+
tesla_fleet_api-0.0.4.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
10
|
+
tesla_fleet_api-0.0.4.dist-info/top_level.txt,sha256=jeNbog_1saXBFrGpom9WyPWmilxsyP3szL_G7JLWQfM,16
|
11
|
+
tesla_fleet_api-0.0.4.dist-info/RECORD,,
|
@@ -1,11 +0,0 @@
|
|
1
|
-
tesla_fleet_api/TeslaFleetApi.py,sha256=gzS7XdhUs2GJtWp0BwH-r-wdiovnPnXsu3PH6FYseIQ,45391
|
2
|
-
tesla_fleet_api/TeslaFleetOAuth.py,sha256=AHzuKlrywD9Hf_nj4_1WcQCIaoxiI4JuD7XAv8dV6tU,469
|
3
|
-
tesla_fleet_api/Teslemetry.py,sha256=66IorPsqvpZ8Uk1bgj_vkoWNLi9kb5HHnNag_iNoBD8,668
|
4
|
-
tesla_fleet_api/__init__.py,sha256=HFzNi2gaIAOSUwE2vO5TgZZAqol47zyhuNOoPXidbVQ,161
|
5
|
-
tesla_fleet_api/const.py,sha256=FohVnzQhy_onvN1qkEVvHsx1ox9BvBsedRxDjaO9WRs,663
|
6
|
-
tesla_fleet_api/exceptions.py,sha256=y-uWOCgZx-BqZHtMGUV5560xNmu9YTNTmXgqK45egcM,8623
|
7
|
-
tesla_fleet_api-0.0.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
8
|
-
tesla_fleet_api-0.0.1.dist-info/METADATA,sha256=zM87ZTGmciNaEg72yqF8XLyY52iSuLOdQDS_CKCUfsY,1284
|
9
|
-
tesla_fleet_api-0.0.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
10
|
-
tesla_fleet_api-0.0.1.dist-info/top_level.txt,sha256=jeNbog_1saXBFrGpom9WyPWmilxsyP3szL_G7JLWQfM,16
|
11
|
-
tesla_fleet_api-0.0.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|