carconnectivity-connector-seatcupra 0.1a7__py3-none-any.whl → 0.1a8__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.
- {carconnectivity_connector_seatcupra-0.1a7.dist-info → carconnectivity_connector_seatcupra-0.1a8.dist-info}/METADATA +4 -1
- {carconnectivity_connector_seatcupra-0.1a7.dist-info → carconnectivity_connector_seatcupra-0.1a8.dist-info}/RECORD +10 -10
- {carconnectivity_connector_seatcupra-0.1a7.dist-info → carconnectivity_connector_seatcupra-0.1a8.dist-info}/WHEEL +1 -1
- carconnectivity_connectors/seatcupra/_version.py +1 -1
- carconnectivity_connectors/seatcupra/auth/my_cupra_session.py +58 -28
- carconnectivity_connectors/seatcupra/auth/session_manager.py +5 -1
- carconnectivity_connectors/seatcupra/auth/vw_web_session.py +3 -0
- carconnectivity_connectors/seatcupra/connector.py +16 -3
- {carconnectivity_connector_seatcupra-0.1a7.dist-info → carconnectivity_connector_seatcupra-0.1a8.dist-info}/LICENSE +0 -0
- {carconnectivity_connector_seatcupra-0.1a7.dist-info → carconnectivity_connector_seatcupra-0.1a8.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: carconnectivity-connector-seatcupra
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.1a8
|
4
4
|
Summary: CarConnectivity connector for Seat and Cupra services
|
5
5
|
Author: Till Steinbach
|
6
6
|
License: MIT License
|
@@ -68,6 +68,7 @@ In your carconnectivity.json configuration add a section for the seatcupra conne
|
|
68
68
|
{
|
69
69
|
"type": "seatcupra",
|
70
70
|
"config": {
|
71
|
+
"brand": "cupra",
|
71
72
|
"username": "test@test.de",
|
72
73
|
"password": "testpassword123"
|
73
74
|
}
|
@@ -76,6 +77,8 @@ In your carconnectivity.json configuration add a section for the seatcupra conne
|
|
76
77
|
}
|
77
78
|
}
|
78
79
|
```
|
80
|
+
`brand` (`seat` or `cupra`) defines what login is used. MyCupra or MySeat account. Your credentials will work with both, but you may need to consent again to the terms and conditions when you use the "wrong" brand.
|
81
|
+
|
79
82
|
### Credentials
|
80
83
|
If you do not want to provide your username or password inside the configuration you have to create a ".netrc" file at the appropriate location (usually this is your home folder):
|
81
84
|
```
|
@@ -1,21 +1,21 @@
|
|
1
1
|
carconnectivity_connectors/seatcupra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
carconnectivity_connectors/seatcupra/_version.py,sha256=
|
2
|
+
carconnectivity_connectors/seatcupra/_version.py,sha256=8cqHsmeL27BhXP-5TXDRomSBRkxC7CZnFEC8NypOGaI,508
|
3
3
|
carconnectivity_connectors/seatcupra/capability.py,sha256=Oe9tC_u69bj6VmOuNJ21RKoETe2j3QyZCoz-VgcZPQ0,4523
|
4
4
|
carconnectivity_connectors/seatcupra/charging.py,sha256=BJe_5GEB0JkP78tpU6kyKpwuwjDZHvm-kt3PTlpQHeU,3336
|
5
5
|
carconnectivity_connectors/seatcupra/climatization.py,sha256=0xxWlxrheAPzkVT8WRQtbm6ExZmVdgW7lUdOXyS_qWY,1695
|
6
6
|
carconnectivity_connectors/seatcupra/command_impl.py,sha256=mtw8ZwJLmf79fPDZ1N3ImLfB8Gt9JPbzjMuIo2y5v3M,2879
|
7
|
-
carconnectivity_connectors/seatcupra/connector.py,sha256=
|
7
|
+
carconnectivity_connectors/seatcupra/connector.py,sha256=MQ5zGNO051feTZUt23J-TJLQUKdOlzG3pYyeOVAGsaw,99318
|
8
8
|
carconnectivity_connectors/seatcupra/vehicle.py,sha256=kiFVbJgq5VQOzf-vSli_2NsMgY0x4pwvJsjPWLGdr1g,3404
|
9
9
|
carconnectivity_connectors/seatcupra/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
carconnectivity_connectors/seatcupra/auth/auth_util.py,sha256=Y81h8fGOMSMgPtE4wI_TI9WgE_s43uaPjRLBBINhj4g,4433
|
11
|
-
carconnectivity_connectors/seatcupra/auth/my_cupra_session.py,sha256=
|
11
|
+
carconnectivity_connectors/seatcupra/auth/my_cupra_session.py,sha256=ybBZ2y7-kUdo5MJ9Zin8UYViC0y3H43H3yOQcDGtWUo,12767
|
12
12
|
carconnectivity_connectors/seatcupra/auth/openid_session.py,sha256=dA0vE2YuckkMPeqJo2dEI0h8_XfohdCgdGkTyshPF7Q,16858
|
13
|
-
carconnectivity_connectors/seatcupra/auth/session_manager.py,sha256=
|
14
|
-
carconnectivity_connectors/seatcupra/auth/vw_web_session.py,sha256=
|
13
|
+
carconnectivity_connectors/seatcupra/auth/session_manager.py,sha256=ZIDvC848T3fy6PgGqCl8A2SzaNhu2YG19Xam5kgp7SA,5635
|
14
|
+
carconnectivity_connectors/seatcupra/auth/vw_web_session.py,sha256=CcI6m68IyRs6WsMDu-IsW3Dj85vyGiMmxvFqNETMHO0,10929
|
15
15
|
carconnectivity_connectors/seatcupra/auth/helpers/blacklist_retry.py,sha256=f3wsiY5bpHDBxp7Va1Mv9nKJ4u3qnCHZZmDu78_AhMk,1251
|
16
16
|
carconnectivity_connectors/seatcupra/ui/connector_ui.py,sha256=SNYnlcGJpbWhuLiIHD2l6H9IfSiMz3IgmvXsdossDnE,1412
|
17
|
-
carconnectivity_connector_seatcupra-0.
|
18
|
-
carconnectivity_connector_seatcupra-0.
|
19
|
-
carconnectivity_connector_seatcupra-0.
|
20
|
-
carconnectivity_connector_seatcupra-0.
|
21
|
-
carconnectivity_connector_seatcupra-0.
|
17
|
+
carconnectivity_connector_seatcupra-0.1a8.dist-info/LICENSE,sha256=PIwI1alwDyOfvEQHdGCm2u9uf_mGE8030xZDfun0xTo,1071
|
18
|
+
carconnectivity_connector_seatcupra-0.1a8.dist-info/METADATA,sha256=OcY0IeJGVQIRGNGjdTUJ32jwKBA77KB_BNed0XxmTEg,5640
|
19
|
+
carconnectivity_connector_seatcupra-0.1a8.dist-info/WHEEL,sha256=nn6H5-ilmfVryoAQl3ZQ2l8SH5imPWFpm1A5FgEuFV4,91
|
20
|
+
carconnectivity_connector_seatcupra-0.1a8.dist-info/top_level.txt,sha256=KqA8GviZsDH4PtmnwSQsz0HB_w-TWkeEHLIRNo5dTaI,27
|
21
|
+
carconnectivity_connector_seatcupra-0.1a8.dist-info/RECORD,,
|
@@ -23,7 +23,7 @@ from carconnectivity_connectors.seatcupra.auth.openid_session import AccessType
|
|
23
23
|
from carconnectivity_connectors.seatcupra.auth.vw_web_session import VWWebSession
|
24
24
|
|
25
25
|
if TYPE_CHECKING:
|
26
|
-
from typing import Tuple, Dict
|
26
|
+
from typing import Tuple, Dict, Any
|
27
27
|
|
28
28
|
|
29
29
|
LOG: logging.Logger = logging.getLogger("carconnectivity.connectors.seatcupra.auth")
|
@@ -33,22 +33,40 @@ class MyCupraSession(VWWebSession):
|
|
33
33
|
"""
|
34
34
|
MyCupraSession class handles the authentication and session management for Cupras's MyCupra service.
|
35
35
|
"""
|
36
|
-
def __init__(self, session_user, **kwargs) -> None:
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
36
|
+
def __init__(self, session_user, is_seat: bool, **kwargs) -> None:
|
37
|
+
self.is_seat: bool = is_seat
|
38
|
+
if self.is_seat:
|
39
|
+
super(MyCupraSession, self).__init__(client_id='99a5b77d-bd88-4d53-b4e5-a539c60694a3@apps_vw-dilab_com',
|
40
|
+
refresh_url='https://identity.vwgroup.io/oidc/v1/token',
|
41
|
+
scope='openid profile nickname birthdate phone',
|
42
|
+
redirect_uri='seat://oauth-callback',
|
43
|
+
state=None,
|
44
|
+
session_user=session_user,
|
45
|
+
**kwargs)
|
46
|
+
|
47
|
+
self.headers = CaseInsensitiveDict({
|
48
|
+
'accept': '*/*',
|
49
|
+
'content-type': 'application/json',
|
50
|
+
'user-agent': 'SEATApp/2.5.0 (com.seat.myseat.ola; build:202410171614; iOS 15.8.3) Alamofire/5.7.0 Mobile',
|
51
|
+
'accept-language': 'de-de',
|
52
|
+
'accept-encoding': 'gzip, deflate, br'
|
53
|
+
})
|
54
|
+
else:
|
55
|
+
super(MyCupraSession, self).__init__(client_id='3c756d46-f1ba-4d78-9f9a-cff0d5292d51@apps_vw-dilab_com',
|
56
|
+
refresh_url='https://identity.vwgroup.io/oidc/v1/token',
|
57
|
+
scope='openid profile nickname birthdate phone',
|
58
|
+
redirect_uri='cupra://oauth-callback',
|
59
|
+
state=None,
|
60
|
+
session_user=session_user,
|
61
|
+
**kwargs)
|
62
|
+
|
63
|
+
self.headers = CaseInsensitiveDict({
|
64
|
+
'accept': '*/*',
|
65
|
+
'content-type': 'application/json',
|
66
|
+
'user-agent': 'CUPRAApp%20-%20Store/20220503 CFNetwork/1333.0.4 Darwin/21.5.0',
|
67
|
+
'accept-language': 'de-de',
|
68
|
+
'accept-encoding': 'gzip, deflate, br'
|
69
|
+
})
|
52
70
|
|
53
71
|
def login(self):
|
54
72
|
super(MyCupraSession, self).login()
|
@@ -57,8 +75,12 @@ class MyCupraSession(VWWebSession):
|
|
57
75
|
# perform web authentication
|
58
76
|
response = self.do_web_auth(authorization_url_str)
|
59
77
|
# fetch tokens from web authentication response
|
60
|
-
self.
|
61
|
-
|
78
|
+
if self.is_seat:
|
79
|
+
return self.fetch_tokens('https://ola.prod.code.seat.cloud.vwgroup.com/authorization/api/v1/token',
|
80
|
+
authorization_response=response)
|
81
|
+
else:
|
82
|
+
self.fetch_tokens('https://identity.vwgroup.io/oidc/v1/token',
|
83
|
+
authorization_response=response)
|
62
84
|
|
63
85
|
def refresh(self) -> None:
|
64
86
|
# refresh tokens from refresh endpoint
|
@@ -92,15 +114,23 @@ class MyCupraSession(VWWebSession):
|
|
92
114
|
|
93
115
|
if self.token is not None and all(key in self.token for key in ('state', 'id_token', 'access_token', 'code')):
|
94
116
|
# Generate json body for token request
|
95
|
-
|
96
|
-
'state': self.token['state'],
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
117
|
+
if self.is_seat:
|
118
|
+
body: Dict[str, Any] = {'state': self.token['state'],
|
119
|
+
'id_token': self.token['id_token'],
|
120
|
+
'redirect_uri': self.redirect_uri,
|
121
|
+
'client_id': self.client_id,
|
122
|
+
'code': self.token['code'],
|
123
|
+
'grant_type': 'authorization_code'
|
124
|
+
}
|
125
|
+
else:
|
126
|
+
body: Dict[str, Any] = {'state': self.token['state'],
|
127
|
+
'id_token': self.token['id_token'],
|
128
|
+
'redirect_uri': self.redirect_uri,
|
129
|
+
'client_id': self.client_id,
|
130
|
+
'client_secret': 'eb8814e641c81a2640ad62eeccec11c98effc9bccd4269ab7af338b50a94b3a2',
|
131
|
+
'code': self.token['code'],
|
132
|
+
'grant_type': 'authorization_code'
|
133
|
+
}
|
104
134
|
|
105
135
|
request_headers: CaseInsensitiveDict = dict(self.headers) # pyright: ignore reportAssignmentType
|
106
136
|
request_headers['content-type'] = 'application/x-www-form-urlencoded; charset=utf-8'
|
@@ -47,11 +47,13 @@ class Service(Enum):
|
|
47
47
|
|
48
48
|
Attributes:
|
49
49
|
MY_CUPRA (str): Represents the 'MyCupra' service.
|
50
|
+
MY_SEAT (str): Represents the 'MySeat' service.
|
50
51
|
|
51
52
|
Methods:
|
52
53
|
__str__() -> str: Returns the string representation of the service.
|
53
54
|
"""
|
54
55
|
MY_CUPRA = 'MyCupra'
|
56
|
+
MY_SEAT = 'MySeat'
|
55
57
|
|
56
58
|
def __str__(self) -> str:
|
57
59
|
return self.value
|
@@ -127,7 +129,9 @@ class SessionManager():
|
|
127
129
|
cache = self.cache[identifier]
|
128
130
|
|
129
131
|
if service == Service.MY_CUPRA:
|
130
|
-
session = MyCupraSession(session_user=session_user, token=token, metadata=metadata, cache=cache)
|
132
|
+
session = MyCupraSession(session_user=session_user, is_seat=False, token=token, metadata=metadata, cache=cache)
|
133
|
+
elif service == Service.MY_SEAT:
|
134
|
+
session = MyCupraSession(session_user=session_user, is_seat=True, token=token, metadata=metadata, cache=cache)
|
131
135
|
else:
|
132
136
|
raise ValueError(f"Unsupported service: {service}")
|
133
137
|
|
@@ -121,6 +121,9 @@ class VWWebSession(OpenIDSession):
|
|
121
121
|
raise RetrievalError('Temporary server error during login')
|
122
122
|
|
123
123
|
if 'Location' not in response.headers:
|
124
|
+
if 'consent' in url:
|
125
|
+
raise AuthenticationError('Could not find Location in headers, probably due to missing consent. '
|
126
|
+
'Check that you configured the correct brand or try visiting: ' + url)
|
124
127
|
raise APICompatibilityError('Forwarding without Location in headers')
|
125
128
|
|
126
129
|
url = response.headers['Location']
|
@@ -129,17 +129,30 @@ class Connector(BaseConnector):
|
|
129
129
|
self.active_config['max_age'] = config['max_age']
|
130
130
|
self.interval._set_value(timedelta(seconds=self.active_config['interval'])) # pylint: disable=protected-access
|
131
131
|
|
132
|
+
if 'brand' in config:
|
133
|
+
if config['brand'] not in ['seat', 'cupra']:
|
134
|
+
raise ValueError('Brand must be either "seat" or "cupra"')
|
135
|
+
self.active_config['brand'] = config['brand']
|
136
|
+
else:
|
137
|
+
self.active_config['brand'] = 'cupra'
|
138
|
+
|
132
139
|
if self.active_config['username'] is None or self.active_config['password'] is None:
|
133
140
|
raise AuthenticationError('Username or password not provided')
|
134
141
|
|
142
|
+
if self.active_config['brand'] == 'cupra':
|
143
|
+
service = Service.MY_CUPRA
|
144
|
+
elif self.active_config['brand'] == 'seat':
|
145
|
+
service = Service.MY_SEAT
|
146
|
+
else:
|
147
|
+
raise ValueError('Brand must be either "seat" or "cupra"')
|
135
148
|
self._manager: SessionManager = SessionManager(tokenstore=car_connectivity.get_tokenstore(), cache=car_connectivity.get_cache())
|
136
|
-
session: requests.Session = self._manager.get_session(
|
137
|
-
|
149
|
+
session: requests.Session = self._manager.get_session(service, SessionUser(username=self.active_config['username'],
|
150
|
+
password=self.active_config['password']))
|
138
151
|
if not isinstance(session, MyCupraSession):
|
139
152
|
raise AuthenticationError('Could not create session')
|
140
153
|
self.session: MyCupraSession = session
|
141
154
|
self.session.retries = 3
|
142
|
-
self.session.timeout =
|
155
|
+
self.session.timeout = 30
|
143
156
|
self.session.refresh()
|
144
157
|
|
145
158
|
self._elapsed: List[timedelta] = []
|
File without changes
|