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.
@@ -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
- class Base(BaseException):
10
- message: str
11
- status: int
12
- error: str | None
13
- error_description: str | None
8
+ message: str
9
+ status: int
10
+ error: str | None
11
+ error_description: str | None
14
12
 
15
- def __init__(self, status: int, data: dict[str, str] | None = None):
16
- self.status = status
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
- message = "The data request or command is unknown."
20
+ class InvalidCommand(TeslaFleetError):
21
+ """The data request or command is unknown."""
24
22
 
25
- class InvalidField(Base):
26
- """A field in the input is not valid."""
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
- class InvalidRequest(Base):
31
- """The request body is not valid, a description giving a more specific error message may be returned."""
27
+ class InvalidField(TeslaFleetError):
28
+ """A field in the input is not valid."""
32
29
 
33
- message = "The request body is not valid"
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
- message = (
39
- "The 'code' in request body is invalid, generate a new one and try again."
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
- class InvalidRedirectUrl(Base):
43
- """Invalid redirect URI/URL. The authorize redirect URI and token redirect URI must match."""
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
- class UnauthorizedClient(Base):
48
- """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."""
41
+ class InvalidAuthCode(TeslaFleetError):
42
+ """The "code" in request body is invalid, generate a new one and try again."""
49
43
 
50
- 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."
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
- message = "The vehicle has turned off remote access."
48
+ class InvalidRedirectUrl(TeslaFleetError):
49
+ """Invalid redirect URI/URL. The authorize redirect URI and token redirect URI must match."""
56
50
 
57
- class NoResponseBody(Base):
58
- """The OAuth token has expired."""
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
- class PaymentRequired(Base):
63
- """Payment is required in order to use the API (non-free account only)."""
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
- message = "Payment is required in order to use the API (non-free account only)."
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
- message = "Access to this resource is not authorized, developers should check required scopes."
62
+ class MobileAccessDisabled(TeslaFleetError):
63
+ """The vehicle has turned off remote access."""
71
64
 
72
- class NotFound(Base):
73
- """The requested resource does not exist."""
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
- class NotAllowed(Base):
78
- """The operation is not allowed."""
69
+ class OAuthExpired(TeslaFleetError):
70
+ """The OAuth token has expired."""
79
71
 
80
- message = "The operation is not allowed."
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
- message = "The HTTP request does not have a Content-Type header set to application/json."
76
+ class PaymentRequired(TeslaFleetError):
77
+ """Payment is required in order to use the API (non-free account only)."""
86
78
 
87
- class VehicleOffline(Base):
88
- """The vehicle is not "online."""
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
- class PreconditionFailed(Base):
93
- """A condition has not been met to process the request."""
83
+ class Forbidden(TeslaFleetError):
84
+ """Access to this resource is not authorized, developers should check required scopes."""
94
85
 
95
- message = "A condition has not been met to process the request."
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
- message = "This user is not present in the current region."
90
+ class NotFound(TeslaFleetError):
91
+ """The requested resource does not exist."""
101
92
 
102
- class InvalidResource(Base):
103
- """There is a semantic problem with the data, e.g. missing or invalid data."""
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
- class Locked(Base):
110
- """Account is locked, and must be unlocked by Tesla."""
97
+ class NotAllowed(TeslaFleetError):
98
+ """The operation is not allowed."""
111
99
 
112
- message = "Account is locked, and must be unlocked by Tesla."
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
- message = "Account or server is rate limited."
104
+ class NotAcceptable(TeslaFleetError):
105
+ """The HTTP request does not have a Content-Type header set to application/json."""
118
106
 
119
- class ResourceUnavailableForLegalReasons(Base):
120
- """Querying for a user/vehicle without proper privacy settings."""
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
- class ClientClosedRequest(Base):
125
- """Client has closed the request before the server could send a response."""
113
+ class VehicleOffline(TeslaFleetError):
114
+ """The vehicle is not "online."""
126
115
 
127
- message = (
128
- "Client has closed the request before the server could send a response."
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
- message = "An error occurred while processing the request."
120
+ class PreconditionFailed(TeslaFleetError):
121
+ """A condition has not been met to process the request."""
135
122
 
136
- class ServiceUnavailable(Base):
137
- """Either an internal service or a vehicle did not respond (timeout)."""
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
- class GatewayTimeout(Base):
142
- """Server did not receive a response."""
127
+ class InvalidRegion(TeslaFleetError):
128
+ """This user is not present in the current region."""
143
129
 
144
- message = "Server did not receive a response."
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
- message = "Vehicle responded with an error - might need a reboot, OTA update, or service."
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
- try:
157
- data = await resp.json()
158
- except json.decoder.JSONDecodeError as error:
159
- raise TeslaFleetError.NotFound() from error
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 TeslaFleetError.InvalidCommand(e.status, data) from e
225
+ raise InvalidCommand(data) from e
166
226
  elif data.error == Errors.INVALID_FIELD:
167
- raise TeslaFleetError.InvalidField(e.status, data) from e
227
+ raise InvalidField(data) from e
168
228
  elif data.error == Errors.INVALID_REQUEST:
169
- raise TeslaFleetError.InvalidRequest(e.status, data) from e
229
+ raise InvalidRequest(data) from e
170
230
  elif data.error == Errors.INVALID_AUTH_CODE:
171
- raise TeslaFleetError.InvalidAuthCode(e.status, data) from e
231
+ raise InvalidAuthCode(data) from e
172
232
  elif data.error == Errors.INVALID_REDIRECT_URL:
173
- raise TeslaFleetError.InvalidRedirectUrl(e.status, data) from e
233
+ raise InvalidRedirectUrl(data) from e
174
234
  elif data.error == Errors.UNAUTHORIZED_CLIENT:
175
- raise TeslaFleetError.UnauthorizedClient(e.status, data) from e
235
+ raise UnauthorizedClient(data) from e
176
236
  elif e.status == 401:
177
237
  if data.error == Errors.MOBILE_ACCESS_DISABLED:
178
- raise TeslaFleetError.MobileAccessDisabled(e.status, data) from e
179
- elif data.error == Errors.NO_RESPONSE_BODY:
180
- raise TeslaFleetError.NoResponseBody(e.status, data) from e
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 TeslaFleetError.PaymentRequired(e.status, data) from e
242
+ raise PaymentRequired(data) from e
183
243
  elif e.status == 403:
184
- raise TeslaFleetError.Forbidden(e.status, data) from e
244
+ raise Forbidden(data) from e
185
245
  elif e.status == 404:
186
- raise TeslaFleetError.NotFound(e.status, data) from e
246
+ raise NotFound(data) from e
187
247
  elif e.status == 405:
188
- raise TeslaFleetError.NotAllowed(e.status, data) from e
248
+ raise NotAllowed(data) from e
189
249
  elif e.status == 406:
190
- raise TeslaFleetError.NotAcceptable(e.status, data) from e
250
+ raise NotAcceptable(data) from e
191
251
  elif e.status == 408:
192
- raise TeslaFleetError.VehicleOffline(e.status, data) from e
252
+ raise VehicleOffline(data) from e
193
253
  elif e.status == 412:
194
- raise TeslaFleetError.PreconditionFailed(e.status, data) from e
254
+ raise PreconditionFailed(data) from e
195
255
  elif e.status == 421:
196
- raise TeslaFleetError.InvalidRegion(e.status, data) from e
256
+ raise InvalidRegion(data) from e
197
257
  elif e.status == 422:
198
- raise TeslaFleetError.InvalidResource(e.status, data) from e
258
+ raise InvalidResource(data) from e
199
259
  elif e.status == 423:
200
- raise TeslaFleetError.Locked(e.status, data) from e
260
+ raise Locked(data) from e
201
261
  elif e.status == 429:
202
- raise TeslaFleetError.RateLimited(e.status, data) from e
262
+ raise RateLimited(data) from e
203
263
  elif e.status == 451:
204
- raise TeslaFleetError.ResourceUnavailableForLegalReasons(
205
- e.status, data
206
- ) from e
264
+ raise ResourceUnavailableForLegalReasons(data) from e
207
265
  elif e.status == 499:
208
- raise TeslaFleetError.ClientClosedRequest(e.status, data) from e
266
+ raise ClientClosedRequest(data) from e
209
267
  elif e.status == 500:
210
- raise TeslaFleetError.InternalServerError(e.status, data) from e
268
+ raise InternalServerError(data) from e
211
269
  elif e.status == 503:
212
- raise TeslaFleetError.ServiceUnavailable(e.status, data) from e
270
+ raise ServiceUnavailable(data) from e
213
271
  elif e.status == 504:
214
- raise TeslaFleetError.GatewayTimeout(e.status, data) from e
272
+ raise GatewayTimeout(data) from e
215
273
  elif e.status == 540:
216
- raise TeslaFleetError.DeviceUnexpectedResponse(e.status, data) from e
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.1
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 End to End encrypted Telemetry or Command API.
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,,