tesla-fleet-api 0.6.1__py3-none-any.whl → 0.7.0__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/__init__.py +1 -0
- tesla_fleet_api/const.py +1 -1
- tesla_fleet_api/teslafleetapi.py +2 -2
- tesla_fleet_api/teslafleetoauth.py +25 -9
- tesla_fleet_api/teslafleetopensource.py +61 -0
- tesla_fleet_api/teslemetry.py +7 -2
- tesla_fleet_api/tessie.py +8 -0
- tesla_fleet_api/vehicle.py +1 -1
- tesla_fleet_api/vehiclespecific.py +8 -0
- {tesla_fleet_api-0.6.1.dist-info → tesla_fleet_api-0.7.0.dist-info}/METADATA +1 -1
- tesla_fleet_api-0.7.0.dist-info/RECORD +20 -0
- {tesla_fleet_api-0.6.1.dist-info → tesla_fleet_api-0.7.0.dist-info}/WHEEL +1 -1
- tesla_fleet_api-0.6.1.dist-info/RECORD +0 -19
- {tesla_fleet_api-0.6.1.dist-info → tesla_fleet_api-0.7.0.dist-info}/LICENSE +0 -0
- {tesla_fleet_api-0.6.1.dist-info → tesla_fleet_api-0.7.0.dist-info}/top_level.txt +0 -0
tesla_fleet_api/__init__.py
CHANGED
tesla_fleet_api/const.py
CHANGED
tesla_fleet_api/teslafleetapi.py
CHANGED
@@ -46,8 +46,6 @@ class TeslaFleetApi:
|
|
46
46
|
if region not in SERVERS:
|
47
47
|
raise ValueError(f"Region must be one of {', '.join(SERVERS.keys())}")
|
48
48
|
self.server = SERVERS.get(region)
|
49
|
-
else:
|
50
|
-
raise ValueError("Either server or region must be provided.")
|
51
49
|
|
52
50
|
LOGGER.debug("Using server %s", self.server)
|
53
51
|
|
@@ -114,6 +112,8 @@ class TeslaFleetApi:
|
|
114
112
|
params=params,
|
115
113
|
) as resp:
|
116
114
|
LOGGER.debug("Response Status: %s", resp.status)
|
115
|
+
if "x-txid" in resp.headers:
|
116
|
+
LOGGER.debug("Response TXID: %s", resp.headers["x-txid"])
|
117
117
|
if not resp.ok:
|
118
118
|
await raise_for_status(resp)
|
119
119
|
if not resp.content_type.lower().startswith("application/json"):
|
@@ -11,11 +11,16 @@ class TeslaFleetOAuth(TeslaFleetApi):
|
|
11
11
|
"""Tesla Fleet OAuth API."""
|
12
12
|
|
13
13
|
expires: int
|
14
|
+
refresh_token: str
|
15
|
+
redirect_uri: str | None
|
16
|
+
_client_secret: str | None
|
14
17
|
|
15
18
|
def __init__(
|
16
19
|
self,
|
17
20
|
session: aiohttp.ClientSession,
|
18
21
|
client_id: str,
|
22
|
+
client_secret: str | None = None,
|
23
|
+
redirect_uri: str | None = None,
|
19
24
|
access_token: str | None = None,
|
20
25
|
refresh_token: str | None = None,
|
21
26
|
expires: int = 0,
|
@@ -23,6 +28,8 @@ class TeslaFleetOAuth(TeslaFleetApi):
|
|
23
28
|
server: str | None = None,
|
24
29
|
):
|
25
30
|
self.client_id = client_id
|
31
|
+
self._client_secret = client_secret
|
32
|
+
self.redirect_uri = redirect_uri
|
26
33
|
self.access_token = access_token
|
27
34
|
self.refresh_token = refresh_token
|
28
35
|
self.expires = expires
|
@@ -34,25 +41,34 @@ class TeslaFleetOAuth(TeslaFleetApi):
|
|
34
41
|
server=server,
|
35
42
|
)
|
36
43
|
|
37
|
-
def get_login_url(
|
38
|
-
self, redirect_uri: str, scopes: list[Scope], state: str = "login"
|
39
|
-
) -> str:
|
44
|
+
def get_login_url(self, scopes: list[Scope], state: str = "login") -> str:
|
40
45
|
"""Get the login URL."""
|
41
|
-
|
46
|
+
if self.redirect_uri is None:
|
47
|
+
raise ValueError("Redirect URI is missing")
|
48
|
+
return f"https://auth.tesla.com/oauth2/v3/authorize?response_type=code&prompt=login&client_id={self.client_id}&redirect_uri={self.redirect_uri}&scope={' '.join(scopes)}&state={state}"
|
42
49
|
|
43
|
-
async def get_refresh_token(
|
44
|
-
self, client_secret: str, code: str, redirect_uri: str
|
45
|
-
) -> None:
|
50
|
+
async def get_refresh_token(self, code: str) -> None:
|
46
51
|
"""Get the refresh token."""
|
52
|
+
|
53
|
+
if self._client_secret is None:
|
54
|
+
raise ValueError("Client secret is missing")
|
55
|
+
|
56
|
+
if self.redirect_uri is None:
|
57
|
+
raise ValueError("Redirect URI is missing")
|
58
|
+
|
59
|
+
if self.server is None:
|
60
|
+
self.region = code.split("_")[0].lower()
|
61
|
+
self.server = SERVERS.get(self.region)
|
62
|
+
|
47
63
|
async with self.session.post(
|
48
64
|
"https://auth.tesla.com/oauth2/v3/token",
|
49
65
|
data={
|
50
66
|
"grant_type": "authorization_code",
|
51
67
|
"client_id": self.client_id,
|
52
|
-
"client_secret":
|
68
|
+
"client_secret": self._client_secret,
|
53
69
|
"code": code,
|
54
70
|
"audience": self.server,
|
55
|
-
"redirect_uri": redirect_uri,
|
71
|
+
"redirect_uri": self.redirect_uri,
|
56
72
|
},
|
57
73
|
) as resp:
|
58
74
|
if resp.ok:
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import aiohttp
|
2
|
+
import time
|
3
|
+
import secrets
|
4
|
+
import hashlib
|
5
|
+
import base64
|
6
|
+
|
7
|
+
from .teslafleetoauth import TeslaFleetOAuth
|
8
|
+
from .const import Scope, SERVERS
|
9
|
+
|
10
|
+
|
11
|
+
class TeslaFleetOpenSource(TeslaFleetOAuth):
|
12
|
+
"""Tesla Fleet Open Source OAuth API."""
|
13
|
+
|
14
|
+
code_verifier: str
|
15
|
+
code_challenge: str
|
16
|
+
|
17
|
+
def __init__(
|
18
|
+
self,
|
19
|
+
session: aiohttp.ClientSession,
|
20
|
+
client_id: str,
|
21
|
+
redirect_uri: str,
|
22
|
+
):
|
23
|
+
self.code_verifier = secrets.token_urlsafe(32)
|
24
|
+
|
25
|
+
# Hash the code_verifier using SHA-256
|
26
|
+
hashed_verifier = hashlib.sha256(self.code_verifier.encode()).digest()
|
27
|
+
# Encode the hash using URL-safe Base64 encoding, without padding
|
28
|
+
self.code_challenge = (
|
29
|
+
base64.urlsafe_b64encode(hashed_verifier).decode().replace("=", "")
|
30
|
+
)
|
31
|
+
|
32
|
+
super().__init__(session, client_id, redirect_uri=redirect_uri)
|
33
|
+
|
34
|
+
def get_login_url(self, scopes: list[Scope], state: str = "login") -> str:
|
35
|
+
"""Get the login URL without a client secret."""
|
36
|
+
|
37
|
+
return (
|
38
|
+
super().get_login_url(scopes, state)
|
39
|
+
+ f"&code_challenge={self.code_challenge}"
|
40
|
+
)
|
41
|
+
|
42
|
+
async def get_refresh_token(self, code: str) -> None:
|
43
|
+
"""Get the refresh token."""
|
44
|
+
async with self.session.post(
|
45
|
+
"https://auth.tesla.com/oauth2/v3/token",
|
46
|
+
data={
|
47
|
+
"grant_type": "authorization_code",
|
48
|
+
"client_id": self.client_id,
|
49
|
+
"code": code,
|
50
|
+
"audience": self.server,
|
51
|
+
"redirect_uri": self.redirect_uri,
|
52
|
+
"code_verifier": self.code_verifier,
|
53
|
+
},
|
54
|
+
) as resp:
|
55
|
+
if resp.ok:
|
56
|
+
data = await resp.json()
|
57
|
+
self.refresh_token = data["refresh_token"]
|
58
|
+
self.access_token = data["access_token"]
|
59
|
+
self.expires = int(time.time()) + data["expires_in"]
|
60
|
+
region = code.split("_")[0].lower()
|
61
|
+
self.server = SERVERS.get(region)
|
tesla_fleet_api/teslemetry.py
CHANGED
@@ -2,7 +2,7 @@ import aiohttp
|
|
2
2
|
from aiolimiter import AsyncLimiter
|
3
3
|
from typing import Any
|
4
4
|
from .teslafleetapi import TeslaFleetApi
|
5
|
-
from .const import Method, LOGGER
|
5
|
+
from .const import Method, LOGGER, Scope
|
6
6
|
|
7
7
|
# Rate limit should be global, even if multiple instances are created
|
8
8
|
rate_limit = AsyncLimiter(5, 10)
|
@@ -39,7 +39,7 @@ class Teslemetry(TeslaFleetApi):
|
|
39
39
|
)
|
40
40
|
|
41
41
|
async def metadata(self, update_region=True) -> dict[str, Any]:
|
42
|
-
"""
|
42
|
+
"""Get user metadata including scopes."""
|
43
43
|
resp = await self._request(
|
44
44
|
Method.GET,
|
45
45
|
"api/metadata",
|
@@ -49,6 +49,11 @@ class Teslemetry(TeslaFleetApi):
|
|
49
49
|
self.server = f"https://{self.region}.teslemetry.com"
|
50
50
|
LOGGER.debug("Using server %s", self.server)
|
51
51
|
return resp
|
52
|
+
|
53
|
+
async def scopes(self) -> list[str]:
|
54
|
+
"""Get user scopes."""
|
55
|
+
resp = await self.metadata(False)
|
56
|
+
return resp["scopes"]
|
52
57
|
|
53
58
|
async def find_server(self) -> str:
|
54
59
|
"""Find the server URL for the Tesla Fleet API."""
|
tesla_fleet_api/tessie.py
CHANGED
@@ -19,6 +19,14 @@ class Tessie(TeslaFleetApi):
|
|
19
19
|
user_scope=False,
|
20
20
|
)
|
21
21
|
|
22
|
+
async def scopes(self) -> list[str]:
|
23
|
+
"""Get user scopes."""
|
24
|
+
resp = await self._request(
|
25
|
+
Method.GET,
|
26
|
+
"auth/tesla_scopes",
|
27
|
+
)
|
28
|
+
return resp["scopes"]
|
29
|
+
|
22
30
|
async def find_server(self) -> str:
|
23
31
|
"""Find the server URL for the Tesla Fleet API."""
|
24
32
|
raise NotImplementedError("Do not use this function for Tessie.")
|
tesla_fleet_api/vehicle.py
CHANGED
@@ -752,7 +752,7 @@ class Vehicle:
|
|
752
752
|
async def fleet_status(self, vins: List[str]) -> dict[str, Any]:
|
753
753
|
"""Checks whether vehicles can accept Tesla commands protocol for the partner's public key"""
|
754
754
|
return await self._request(
|
755
|
-
Method.
|
755
|
+
Method.POST, "api/1/vehicles/fleet_status", json={"vins": vins}
|
756
756
|
)
|
757
757
|
|
758
758
|
async def fleet_telemetry_config_create(
|
@@ -436,6 +436,14 @@ class VehicleSpecific:
|
|
436
436
|
"""Checks whether vehicles can accept Tesla commands protocol for the partner's public key"""
|
437
437
|
return await self._parent.fleet_status([self.vin])
|
438
438
|
|
439
|
+
async def fleet_telemetry_config_create(
|
440
|
+
self, config: dict[str, Any]
|
441
|
+
) -> dict[str, Any]:
|
442
|
+
"""Configures fleet telemetry."""
|
443
|
+
return await self._parent.fleet_telemetry_config_create(
|
444
|
+
{"vins": [self.vin], "config": config}
|
445
|
+
)
|
446
|
+
|
439
447
|
async def fleet_telemetry_config_get(self) -> dict[str, Any]:
|
440
448
|
"""Configures fleet telemetry."""
|
441
449
|
return await self._parent.fleet_telemetry_config_get(self.vin)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
tesla_fleet_api/__init__.py,sha256=-OhrvoMVLeo13dDCjsltCacLxPqe2UODvSgHFdjyteA,639
|
2
|
+
tesla_fleet_api/charging.py,sha256=N_mc8axrXj3iduqLj_jCt4Vx86tHqe3xqQT4R1R7HvU,1689
|
3
|
+
tesla_fleet_api/const.py,sha256=oHOrKlioHra7rwxN0vNjw-ogFxmwuluUduYbX2aEbM8,9277
|
4
|
+
tesla_fleet_api/energy.py,sha256=yOKNPyXWKMT7Z9a5RqdNksHokLRo657SWjly1bP2_Qs,5827
|
5
|
+
tesla_fleet_api/energyspecific.py,sha256=zggN-q0tf6EH_57bM4EuQ-IdcemKP0o-xv__LArRNnA,4147
|
6
|
+
tesla_fleet_api/exceptions.py,sha256=BcvJyjY4qM2-Rf7ZMA94xPIN6ebKit89E7qattMmbr8,11018
|
7
|
+
tesla_fleet_api/partner.py,sha256=1vIBUaxKLIfqcC0X6VXZN0dMAzj_CLNPUMjA6QVqZ1k,1223
|
8
|
+
tesla_fleet_api/teslafleetapi.py,sha256=S-7TH47aTuCi1LV4fLCxtvcCxuA0vyGC04XYBKHYG_4,4853
|
9
|
+
tesla_fleet_api/teslafleetoauth.py,sha256=OY9yRQuokYo3ts0C8Qb6Z-o9NNAGHbX9F5mHfAh50fo,4121
|
10
|
+
tesla_fleet_api/teslafleetopensource.py,sha256=TJfVPcqJlA1b3kMoGuLr-g5Gn8UDyYsTZhjvGY1MtIk,2007
|
11
|
+
tesla_fleet_api/teslemetry.py,sha256=kcZG7O9tsBt0BoUyCUSU9j9yCbN1qJDL1iUUo4DFESs,2167
|
12
|
+
tesla_fleet_api/tessie.py,sha256=4dBYxe1G2v9JvJGRbb01wXrAmvWT4jOfV4f_VQE_vkE,2302
|
13
|
+
tesla_fleet_api/user.py,sha256=TZE2oh-n5zrhKXmGRuiNL9voKVODD7rBhGE_IObYVGA,1179
|
14
|
+
tesla_fleet_api/vehicle.py,sha256=KFFotHSmzaC4MhIlU8hoG7SVvPiV3_FC__uJf8BG_G0,31412
|
15
|
+
tesla_fleet_api/vehiclespecific.py,sha256=Nr4zZzfmIuw3RFYjQEX6c_xtYZgztMsN5ohVn-YEH0I,20600
|
16
|
+
tesla_fleet_api-0.7.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
17
|
+
tesla_fleet_api-0.7.0.dist-info/METADATA,sha256=xJ4Byt3bVHwF_50-6-_WmqP9Z3OkTtiGKwS46UHltog,3821
|
18
|
+
tesla_fleet_api-0.7.0.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
|
19
|
+
tesla_fleet_api-0.7.0.dist-info/top_level.txt,sha256=jeNbog_1saXBFrGpom9WyPWmilxsyP3szL_G7JLWQfM,16
|
20
|
+
tesla_fleet_api-0.7.0.dist-info/RECORD,,
|
@@ -1,19 +0,0 @@
|
|
1
|
-
tesla_fleet_api/__init__.py,sha256=0MON9vh3AShIiX16FZ6NU3yZ7kyXFh5GxA0rY8CzVRM,584
|
2
|
-
tesla_fleet_api/charging.py,sha256=N_mc8axrXj3iduqLj_jCt4Vx86tHqe3xqQT4R1R7HvU,1689
|
3
|
-
tesla_fleet_api/const.py,sha256=LjJDHWvu3SJL5weMizfTKIZ9bbQtAHPjPz-EkhrtuVU,9277
|
4
|
-
tesla_fleet_api/energy.py,sha256=yOKNPyXWKMT7Z9a5RqdNksHokLRo657SWjly1bP2_Qs,5827
|
5
|
-
tesla_fleet_api/energyspecific.py,sha256=zggN-q0tf6EH_57bM4EuQ-IdcemKP0o-xv__LArRNnA,4147
|
6
|
-
tesla_fleet_api/exceptions.py,sha256=BcvJyjY4qM2-Rf7ZMA94xPIN6ebKit89E7qattMmbr8,11018
|
7
|
-
tesla_fleet_api/partner.py,sha256=1vIBUaxKLIfqcC0X6VXZN0dMAzj_CLNPUMjA6QVqZ1k,1223
|
8
|
-
tesla_fleet_api/teslafleetapi.py,sha256=-hiZfEEdS8Sfj_yqW-woiD5XD5_Q2IcMubmkh7wdtdE,4826
|
9
|
-
tesla_fleet_api/teslafleetoauth.py,sha256=OC6djQw2ieZR_rWK5HoInLScZOu5T5aMClbg5gqbnDg,3502
|
10
|
-
tesla_fleet_api/teslemetry.py,sha256=07vY6y56slUXvyaQDGQSGW_VF98fQUgEh-xXrG_phw8,2000
|
11
|
-
tesla_fleet_api/tessie.py,sha256=dZs85N2uVlqkjxB3ICRVnPyyJkl4tWDAb3CQqV4MqQc,2096
|
12
|
-
tesla_fleet_api/user.py,sha256=TZE2oh-n5zrhKXmGRuiNL9voKVODD7rBhGE_IObYVGA,1179
|
13
|
-
tesla_fleet_api/vehicle.py,sha256=EE8ZgL1QdOVfwPsShGeI9DZtsKcLKG7EcfBCnEugfH0,31411
|
14
|
-
tesla_fleet_api/vehiclespecific.py,sha256=BhWaQGsNhg06gSLGqNEs2JQgolY3mNGtOWz478lofFE,20324
|
15
|
-
tesla_fleet_api-0.6.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
16
|
-
tesla_fleet_api-0.6.1.dist-info/METADATA,sha256=cmPk7ahou15PI_wS_E1yfkXDxhpZtcBGYdbFklJREUk,3821
|
17
|
-
tesla_fleet_api-0.6.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
18
|
-
tesla_fleet_api-0.6.1.dist-info/top_level.txt,sha256=jeNbog_1saXBFrGpom9WyPWmilxsyP3szL_G7JLWQfM,16
|
19
|
-
tesla_fleet_api-0.6.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|