tesla-fleet-api 0.6.1__py3-none-any.whl → 0.7.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,6 @@
1
1
  from .teslafleetapi import TeslaFleetApi
2
2
  from .teslafleetoauth import TeslaFleetOAuth
3
+ from .teslafleetopensource import TeslaFleetOpenSource
3
4
  from .teslemetry import Teslemetry
4
5
  from .tessie import Tessie
5
6
  from .charging import Charging
tesla_fleet_api/const.py CHANGED
@@ -3,7 +3,7 @@
3
3
  from enum import Enum
4
4
  import logging
5
5
 
6
- VERSION = "0.6.1"
6
+ VERSION = "0.7.0"
7
7
  LOGGER = logging.getLogger(__package__)
8
8
  SERVERS = {
9
9
  "na": "https://fleet-api.prd.na.vn.cloud.tesla.com",
@@ -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
- return f"https://auth.tesla.com/oauth2/v3/authorize?response_type=code&client_id={self.client_id}&redirect_uri={redirect_uri}&scope={' '.join(scopes)}&state={state}"
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": 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)
@@ -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
- """Test API Authentication."""
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.")
@@ -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.GET, "api/1/vehicles/fleet_status", json={"vins": vins}
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tesla_fleet_api
3
- Version: 0.6.1
3
+ Version: 0.7.0
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
@@ -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,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (70.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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,,