carconnectivity-connector-seatcupra 0.1a18__tar.gz → 0.1.2__tar.gz

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.
Files changed (40) hide show
  1. carconnectivity_connector_seatcupra-0.1.2/CHANGELOG.md +20 -0
  2. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/PKG-INFO +2 -5
  3. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/README.md +0 -3
  4. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/pyproject.toml +1 -1
  5. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connector_seatcupra.egg-info/PKG-INFO +2 -5
  6. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connector_seatcupra.egg-info/requires.txt +1 -1
  7. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/_version.py +2 -2
  8. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/auth/my_cupra_session.py +44 -23
  9. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/auth/openid_session.py +2 -1
  10. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/capability.py +11 -4
  11. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/connector.py +75 -9
  12. carconnectivity_connector_seatcupra-0.1a18/CHANGELOG.md +0 -12
  13. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/.flake8 +0 -0
  14. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  15. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  16. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/.github/dependabot.yml +0 -0
  17. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/.github/workflows/build.yml +0 -0
  18. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/.github/workflows/build_and_publish.yml +0 -0
  19. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/.github/workflows/codeql-analysis.yml +0 -0
  20. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/.gitignore +0 -0
  21. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/LICENSE +0 -0
  22. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/Makefile +0 -0
  23. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/doc/Config.md +0 -0
  24. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/setup.cfg +0 -0
  25. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/setup_requirements.txt +0 -0
  26. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connector_seatcupra.egg-info/SOURCES.txt +0 -0
  27. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connector_seatcupra.egg-info/dependency_links.txt +0 -0
  28. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connector_seatcupra.egg-info/top_level.txt +0 -0
  29. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/__init__.py +0 -0
  30. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/auth/__init__.py +0 -0
  31. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/auth/auth_util.py +0 -0
  32. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/auth/helpers/blacklist_retry.py +0 -0
  33. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/auth/session_manager.py +0 -0
  34. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/auth/vw_web_session.py +0 -0
  35. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/charging.py +0 -0
  36. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/climatization.py +0 -0
  37. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/command_impl.py +0 -0
  38. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/ui/connector_ui.py +0 -0
  39. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/src/carconnectivity_connectors/seatcupra/vehicle.py +0 -0
  40. {carconnectivity_connector_seatcupra-0.1a18 → carconnectivity_connector_seatcupra-0.1.2}/test/integration_test/carConnectivity.json +0 -0
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [Unreleased]
6
+ - No unreleased changes so far
7
+
8
+ ## [0.1.1] - 2025-03-04
9
+ ### Fixed
10
+ - Fixed potential http error when parking position was fetched but due to error not available
11
+
12
+ ### Added
13
+ - Added connection state and vehicle state to the public API
14
+
15
+ ## [0.1] - 2025-03-02
16
+ Initial release, let's go and give this to the public to try out...
17
+
18
+ [unreleased]: https://github.com/tillsteinbach/CarConnectivity-connector-seatcupra/compare/v0.1.1...HEAD
19
+ [0.1.1]: https://github.com/tillsteinbach/CarConnectivity-connector-seatcupra/releases/tag/v0.1.1
20
+ [0.1]: https://github.com/tillsteinbach/CarConnectivity-connector-seatcupra/releases/tag/v0.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: carconnectivity-connector-seatcupra
3
- Version: 0.1a18
3
+ Version: 0.1.2
4
4
  Summary: CarConnectivity connector for Seat and Cupra services
5
5
  Author: Till Steinbach
6
6
  License: MIT License
@@ -37,7 +37,7 @@ Classifier: Topic :: Software Development :: Libraries
37
37
  Requires-Python: >=3.9
38
38
  Description-Content-Type: text/markdown
39
39
  License-File: LICENSE
40
- Requires-Dist: carconnectivity>=0.4a11
40
+ Requires-Dist: carconnectivity>=0.4
41
41
  Requires-Dist: oauthlib~=3.2.2
42
42
  Requires-Dist: requests~=2.32.3
43
43
  Requires-Dist: jwt~=1.3.1
@@ -54,9 +54,6 @@ Requires-Dist: jwt~=1.3.1
54
54
  [![Donate at PayPal](https://img.shields.io/badge/Donate-PayPal-2997d8)](https://www.paypal.com/donate?hosted_button_id=2BVFF5GJ9SXAJ)
55
55
  [![Sponsor at Github](https://img.shields.io/badge/Sponsor-GitHub-28a745)](https://github.com/sponsors/tillsteinbach)
56
56
 
57
-
58
- ## Due to lack of access to a Cupra car the development of this conenctor is currently stuck. If you want to help me with access to your account, please contact me!
59
-
60
57
  [CarConnectivity](https://github.com/tillsteinbach/CarConnectivity) is a python API to connect to various car services. This connector enables the integration of seat and cupra vehicles through the MyCupra API. Look at [CarConnectivity](https://github.com/tillsteinbach/CarConnectivity) for other supported brands.
61
58
 
62
59
  ## Configuration
@@ -10,9 +10,6 @@
10
10
  [![Donate at PayPal](https://img.shields.io/badge/Donate-PayPal-2997d8)](https://www.paypal.com/donate?hosted_button_id=2BVFF5GJ9SXAJ)
11
11
  [![Sponsor at Github](https://img.shields.io/badge/Sponsor-GitHub-28a745)](https://github.com/sponsors/tillsteinbach)
12
12
 
13
-
14
- ## Due to lack of access to a Cupra car the development of this conenctor is currently stuck. If you want to help me with access to your account, please contact me!
15
-
16
13
  [CarConnectivity](https://github.com/tillsteinbach/CarConnectivity) is a python API to connect to various car services. This connector enables the integration of seat and cupra vehicles through the MyCupra API. Look at [CarConnectivity](https://github.com/tillsteinbach/CarConnectivity) for other supported brands.
17
14
 
18
15
  ## Configuration
@@ -14,7 +14,7 @@ authors = [
14
14
  { name = "Till Steinbach" }
15
15
  ]
16
16
  dependencies = [
17
- "carconnectivity>=0.4a11",
17
+ "carconnectivity>=0.4",
18
18
  "oauthlib~=3.2.2",
19
19
  "requests~=2.32.3",
20
20
  "jwt~=1.3.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: carconnectivity-connector-seatcupra
3
- Version: 0.1a18
3
+ Version: 0.1.2
4
4
  Summary: CarConnectivity connector for Seat and Cupra services
5
5
  Author: Till Steinbach
6
6
  License: MIT License
@@ -37,7 +37,7 @@ Classifier: Topic :: Software Development :: Libraries
37
37
  Requires-Python: >=3.9
38
38
  Description-Content-Type: text/markdown
39
39
  License-File: LICENSE
40
- Requires-Dist: carconnectivity>=0.4a11
40
+ Requires-Dist: carconnectivity>=0.4
41
41
  Requires-Dist: oauthlib~=3.2.2
42
42
  Requires-Dist: requests~=2.32.3
43
43
  Requires-Dist: jwt~=1.3.1
@@ -54,9 +54,6 @@ Requires-Dist: jwt~=1.3.1
54
54
  [![Donate at PayPal](https://img.shields.io/badge/Donate-PayPal-2997d8)](https://www.paypal.com/donate?hosted_button_id=2BVFF5GJ9SXAJ)
55
55
  [![Sponsor at Github](https://img.shields.io/badge/Sponsor-GitHub-28a745)](https://github.com/sponsors/tillsteinbach)
56
56
 
57
-
58
- ## Due to lack of access to a Cupra car the development of this conenctor is currently stuck. If you want to help me with access to your account, please contact me!
59
-
60
57
  [CarConnectivity](https://github.com/tillsteinbach/CarConnectivity) is a python API to connect to various car services. This connector enables the integration of seat and cupra vehicles through the MyCupra API. Look at [CarConnectivity](https://github.com/tillsteinbach/CarConnectivity) for other supported brands.
61
58
 
62
59
  ## Configuration
@@ -1,4 +1,4 @@
1
- carconnectivity>=0.4a11
1
+ carconnectivity>=0.4
2
2
  oauthlib~=3.2.2
3
3
  requests~=2.32.3
4
4
  jwt~=1.3.1
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.1a18'
21
- __version_tuple__ = version_tuple = (0, 1)
20
+ __version__ = version = '0.1.2'
21
+ __version_tuple__ = version_tuple = (0, 1, 2)
@@ -46,6 +46,7 @@ class MyCupraSession(VWWebSession):
46
46
 
47
47
  self.headers = CaseInsensitiveDict({
48
48
  'accept': '*/*',
49
+ 'connection': 'keep-alive',
49
50
  'content-type': 'application/json',
50
51
  'user-agent': 'SEATApp/2.5.0 (com.seat.myseat.ola; build:202410171614; iOS 15.8.3) Alamofire/5.7.0 Mobile',
51
52
  'accept-language': 'de-de',
@@ -62,6 +63,7 @@ class MyCupraSession(VWWebSession):
62
63
 
63
64
  self.headers = CaseInsensitiveDict({
64
65
  'accept': '*/*',
66
+ 'connection': 'keep-alive',
65
67
  'content-type': 'application/json',
66
68
  'user-agent': 'CUPRAApp%20-%20Store/20220503 CFNetwork/1333.0.4 Darwin/21.5.0',
67
69
  'accept-language': 'de-de',
@@ -72,12 +74,15 @@ class MyCupraSession(VWWebSession):
72
74
  super(MyCupraSession, self).login()
73
75
  # retrieve authorization URL
74
76
  authorization_url_str: str = self.authorization_url(url='https://identity.vwgroup.io/oidc/v1/authorize')
75
- # perform web authentication
76
- response = self.do_web_auth(authorization_url_str)
77
+ if self.redirect_uri is not None and authorization_url_str.startswith(self.redirect_uri):
78
+ response = authorization_url_str.replace(self.redirect_uri + '#', 'https://egal?')
79
+ else:
80
+ # perform web authentication
81
+ response = self.do_web_auth(authorization_url_str)
77
82
  # fetch tokens from web authentication response
78
83
  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)
84
+ self.fetch_tokens('https://ola.prod.code.seat.cloud.vwgroup.com/authorization/api/v1/token',
85
+ authorization_response=response)
81
86
  else:
82
87
  self.fetch_tokens('https://identity.vwgroup.io/oidc/v1/token',
83
88
  authorization_response=response)
@@ -217,27 +222,43 @@ class MyCupraSession(VWWebSession):
217
222
  if headers is None:
218
223
  headers = dict(self.headers)
219
224
 
220
- body: Dict[str, str] = {
221
- 'client_id': self.client_id,
222
- 'client_secret': 'eb8814e641c81a2640ad62eeccec11c98effc9bccd4269ab7af338b50a94b3a2',
223
- 'grant_type': 'refresh_token',
224
- 'refresh_token': self.refresh_token
225
- }
225
+ if self.is_seat:
226
+ body: Dict[str, str] = {
227
+ 'client_id': self.client_id,
228
+ 'grant_type': 'refresh_token',
229
+ 'refresh_token': self.refresh_token
230
+ }
231
+ else:
232
+ body: Dict[str, str] = {
233
+ 'client_id': self.client_id,
234
+ 'client_secret': 'eb8814e641c81a2640ad62eeccec11c98effc9bccd4269ab7af338b50a94b3a2',
235
+ 'grant_type': 'refresh_token',
236
+ 'refresh_token': self.refresh_token
237
+ }
226
238
 
227
239
  headers['content-type'] = 'application/x-www-form-urlencoded; charset=utf-8'
228
240
 
229
- # Request new tokens using the refresh token
230
- token_response = self.post(
231
- token_url,
232
- data=body,
233
- auth=auth,
234
- timeout=timeout,
235
- headers=headers,
236
- verify=verify,
237
- withhold_token=False, # pyright: ignore reportCallIssue
238
- proxies=proxies,
239
- access_type=AccessType.NONE # pyright: ignore reportCallIssue
240
- )
241
+ tries = 0
242
+ while True:
243
+ try:
244
+ # Request new tokens using the refresh token
245
+ token_response = self.post(
246
+ token_url,
247
+ data=body,
248
+ auth=auth,
249
+ timeout=timeout,
250
+ headers=headers,
251
+ verify=verify,
252
+ withhold_token=False, # pyright: ignore reportCallIssue
253
+ proxies=proxies,
254
+ access_type=AccessType.NONE # pyright: ignore reportCallIssue
255
+ )
256
+ except requests.exceptions.RequestException as err:
257
+ tries += 1
258
+ if tries >= 3:
259
+ raise TemporaryAuthenticationError('Token could not be refreshed due to temporary MyCupra failure') from err
260
+ else:
261
+ break
241
262
  if token_response.status_code == requests.codes['unauthorized']:
242
263
  raise AuthenticationError('Refreshing tokens failed: Server requests new authorization')
243
264
  elif token_response.status_code in (requests.codes['internal_server_error'], requests.codes['service_unavailable'], requests.codes['gateway_timeout']):
@@ -263,7 +284,7 @@ class MyCupraSession(VWWebSession):
263
284
  token=None,
264
285
  timeout=None,
265
286
  **kwargs
266
- ):
287
+ ) -> requests.Response:
267
288
  """Intercept all requests and add userId if present."""
268
289
  if not is_secure_transport(url):
269
290
  raise InsecureTransportError()
@@ -116,7 +116,8 @@ class OpenIDSession(requests.Session):
116
116
  backoff_factor=0.1,
117
117
  status_forcelist=[500],
118
118
  status_blacklist=[429],
119
- raise_on_status=False)
119
+ raise_on_status=False,
120
+ allowed_methods=None) # set allowed_methods to None to retry on all methods
120
121
  self.mount('https://', HTTPAdapter(max_retries=retries))
121
122
 
122
123
  @property
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
5
5
  from enum import IntEnum
6
6
 
7
7
  from carconnectivity.objects import GenericObject
8
- from carconnectivity.attributes import StringAttribute, BooleanAttribute, DateAttribute
8
+ from carconnectivity.attributes import StringAttribute, BooleanAttribute, DateAttribute, GenericAttribute
9
9
 
10
10
  if TYPE_CHECKING:
11
11
  from typing import Dict, Optional, List
@@ -77,7 +77,7 @@ class Capabilities(GenericObject):
77
77
  """
78
78
  return self.__capabilities.get(capability_id)
79
79
 
80
- def has_capability(self, capability_id: str) -> bool:
80
+ def has_capability(self, capability_id: str, check_status_ok=False) -> bool:
81
81
  """
82
82
  Check if the Capabilities contains a capability with the specified ID.
83
83
 
@@ -87,7 +87,14 @@ class Capabilities(GenericObject):
87
87
  Returns:
88
88
  bool: True if the capability exists, otherwise False.
89
89
  """
90
- return capability_id in self.__capabilities
90
+ if check_status_ok:
91
+ if capability_id in self.__capabilities and self.__capabilities[capability_id].enabled:
92
+ capability: Capability = self.__capabilities[capability_id]
93
+ if capability.status.enabled and capability.status.value is not None and len(capability.status.value) > 0:
94
+ return False
95
+ return True
96
+ return False
97
+ return capability_id in self.__capabilities and self.__capabilities[capability_id].enabled
91
98
 
92
99
 
93
100
  class Capability(GenericObject):
@@ -105,7 +112,7 @@ class Capability(GenericObject):
105
112
  self.capability_id = StringAttribute("id", self, capability_id, tags={'connector_custom'})
106
113
  self.expiration_date = DateAttribute("expiration_date", self, tags={'connector_custom'})
107
114
  self.editable = BooleanAttribute("editable", self, tags={'connector_custom'})
108
- self.statuses: List[Capability.Status] = []
115
+ self.status = GenericAttribute("status", self, value=[], tags={'connector_custom'})
109
116
  self.parameters: Dict[str, bool] = {}
110
117
  self.enabled = True
111
118
  self.delay_notifications = False
@@ -288,16 +288,32 @@ class Connector(BaseConnector):
288
288
  vehicle_to_update = self.fetch_vehicle_status(vehicle_to_update)
289
289
  vehicle_to_update = self.fetch_vehicle_mycar_status(vehicle_to_update)
290
290
  vehicle_to_update = self.fetch_mileage(vehicle_to_update)
291
- if vehicle_to_update.capabilities.has_capability('climatisation'):
291
+ if vehicle_to_update.capabilities.has_capability('climatisation', check_status_ok=True):
292
292
  vehicle_to_update = self.fetch_climatisation(vehicle_to_update)
293
- if vehicle_to_update.capabilities.has_capability('charging'):
293
+ if vehicle_to_update.capabilities.has_capability('charging', check_status_ok=True):
294
294
  vehicle_to_update = self.fetch_charging(vehicle_to_update)
295
- if vehicle_to_update.capabilities.has_capability('parkingPosition'):
295
+ if vehicle_to_update.capabilities.has_capability('parkingPosition', check_status_ok=True):
296
296
  vehicle_to_update = self.fetch_parking_position(vehicle_to_update)
297
- if vehicle_to_update.capabilities.has_capability('vehicleHealthInspection'):
297
+ if vehicle_to_update.capabilities.has_capability('vehicleHealthInspection', check_status_ok=True):
298
298
  vehicle_to_update = self.fetch_maintenance(vehicle_to_update)
299
+ vehicle_to_update = self.fetch_connection_status(vehicle_to_update)
300
+ self.decide_state(vehicle_to_update)
299
301
  self.car_connectivity.transaction_end()
300
302
 
303
+ def decide_state(self, vehicle: SeatCupraVehicle) -> None:
304
+ """
305
+ Decides the state of the vehicle based on the current data.
306
+
307
+ Args:
308
+ vehicle (SeatCupraVehicle): The SeatCupra vehicle object.
309
+ """
310
+ if vehicle is not None:
311
+ if vehicle.position is not None and vehicle.position.enabled and vehicle.position.position_type is not None \
312
+ and vehicle.position.position_type.enabled and vehicle.position.position_type.value == Position.PositionType.PARKING:
313
+ vehicle.state._set_value(GenericVehicle.State.PARKED) # pylint: disable=protected-access
314
+ else:
315
+ vehicle.state._set_value(None) # pylint: disable=protected-access
316
+
301
317
  def fetch_vehicles(self) -> None:
302
318
  """
303
319
  Fetches the list of vehicles from the Seat/Cupra Connect API and updates the garage with new vehicles.
@@ -369,6 +385,19 @@ class Connector(BaseConnector):
369
385
  else:
370
386
  capability = Capability(capability_id=capability_id, capabilities=vehicle.capabilities)
371
387
  vehicle.capabilities.add_capability(capability_id, capability)
388
+ if 'status' in capability_dict and capability_dict['status'] is not None:
389
+ statuses = capability_dict['status']
390
+ if isinstance(statuses, list):
391
+ for status in statuses:
392
+ if status in [item.value for item in Capability.Status]:
393
+ capability.status.value.append(Capability.Status(status))
394
+ else:
395
+ LOG_API.warning('Capability status unkown %s', status)
396
+ capability.status.value.append(Capability.Status.UNKNOWN)
397
+ else:
398
+ LOG_API.warning('Capability status not a list in %s', statuses)
399
+ else:
400
+ capability.status.value.clear()
372
401
  if 'expirationDate' in capability_dict and capability_dict['expirationDate'] is not None \
373
402
  and capability_dict['expirationDate'] != '':
374
403
  expiration_date: datetime = robust_time_parse(capability_dict['expirationDate'])
@@ -390,7 +419,7 @@ class Connector(BaseConnector):
390
419
  for capability_id in vehicle.capabilities.capabilities.keys() - found_capabilities:
391
420
  vehicle.capabilities.remove_capability(capability_id)
392
421
 
393
- if vehicle.capabilities.has_capability('charging'):
422
+ if vehicle.capabilities.has_capability('charging', check_status_ok=True):
394
423
  if not isinstance(vehicle, SeatCupraElectricVehicle):
395
424
  LOG.debug('Promoting %s to SeatCupraElectricVehicle object for %s', vehicle.__class__.__name__, vin)
396
425
  vehicle = SeatCupraElectricVehicle(origin=vehicle)
@@ -401,7 +430,7 @@ class Connector(BaseConnector):
401
430
  charging_start_stop_command.enabled = True
402
431
  vehicle.charging.commands.add_command(charging_start_stop_command)
403
432
 
404
- if vehicle.capabilities.has_capability('climatisation'):
433
+ if vehicle.capabilities.has_capability('climatisation', check_status_ok=True):
405
434
  if vehicle.climatization is not None and vehicle.climatization.commands is not None \
406
435
  and not vehicle.climatization.commands.contains_command('start-stop'):
407
436
  climatisation_start_stop_command: ClimatizationStartStopCommand = \
@@ -411,7 +440,7 @@ class Connector(BaseConnector):
411
440
  climatisation_start_stop_command.enabled = True
412
441
  vehicle.climatization.commands.add_command(climatisation_start_stop_command)
413
442
 
414
- if vehicle.capabilities.has_capability('vehicleWakeUpTrigger'):
443
+ if vehicle.capabilities.has_capability('vehicleWakeUpTrigger', check_status_ok=True):
415
444
  if vehicle.commands is not None and vehicle.commands.commands is not None \
416
445
  and not vehicle.commands.contains_command('wake-sleep'):
417
446
  wake_sleep_command = WakeSleepCommand(parent=vehicle.commands)
@@ -420,7 +449,7 @@ class Connector(BaseConnector):
420
449
  vehicle.commands.add_command(wake_sleep_command)
421
450
 
422
451
  # Add honkAndFlash command if necessary capabilities are available
423
- if vehicle.capabilities.has_capability('honkAndFlash'):
452
+ if vehicle.capabilities.has_capability('honkAndFlash', check_status_ok=True):
424
453
  if vehicle.commands is not None and vehicle.commands.commands is not None \
425
454
  and not vehicle.commands.contains_command('honk-flash'):
426
455
  honk_flash_command = HonkAndFlashCommand(parent=vehicle.commands, with_duration=True)
@@ -429,7 +458,7 @@ class Connector(BaseConnector):
429
458
  vehicle.commands.add_command(honk_flash_command)
430
459
 
431
460
  # Add lock and unlock command
432
- if vehicle.capabilities.has_capability('access'):
461
+ if vehicle.capabilities.has_capability('access', check_status_ok=True):
433
462
  if vehicle.doors is not None and vehicle.doors.commands is not None and vehicle.doors.commands.commands is not None \
434
463
  and not vehicle.doors.commands.contains_command('lock-unlock'):
435
464
  lock_unlock_command = LockUnlockCommand(parent=vehicle.doors.commands)
@@ -710,6 +739,43 @@ class Connector(BaseConnector):
710
739
  'remainingTime'})
711
740
  return vehicle
712
741
 
742
+ def fetch_connection_status(self, vehicle: SeatCupraVehicle, no_cache: bool = False) -> SeatCupraVehicle:
743
+ """
744
+ Fetches the connection status of the given Seat/Cupra vehicle and updates its connection attributes.
745
+
746
+ Args:
747
+ vehicle (SeatCupraVehicle): The Seat/Cupra vehicle object containing the VIN and connection attributes.
748
+
749
+ Returns:
750
+ SeatCupraVehicle: The updated Seat/Cupra vehicle object with the fetched connection data.
751
+
752
+ Raises:
753
+ APIError: If the VIN is missing.
754
+ ValueError: If the vehicle has no connection object.
755
+ """
756
+ vin = vehicle.vin.value
757
+ if vin is None:
758
+ raise APIError('VIN is missing')
759
+ url = f'https://ola.prod.code.seat.cloud.vwgroup.com/vehicles/{vin}/connection'
760
+ data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
761
+ # {'connection': {'mode': 'online'}
762
+ if data is not None:
763
+ if 'connection' in data and data['connection'] is not None:
764
+ if 'mode' in data['connection'] and data['connection']['mode'] is not None:
765
+ if data['connection']['mode'] in [item.value for item in GenericVehicle.ConnectionState]:
766
+ connection_state: GenericVehicle.ConnectionState = GenericVehicle.ConnectionState(data['connection']['mode'])
767
+ vehicle.connection_state._set_value(connection_state) # pylint: disable=protected-access
768
+ else:
769
+ vehicle.connection_state._set_value(GenericVehicle.ConnectionState.UNKNOWN) # pylint: disable=protected-access
770
+ LOG_API.info('Unknown connection state %s', data['connection']['mode'])
771
+ else:
772
+ vehicle.connection_state._set_value(None) # pylint: disable=protected-access
773
+ log_extra_keys(LOG_API, 'connection status', data['connection'], {'mode'})
774
+ else:
775
+ vehicle.connection_state._set_value(None) # pylint: disable=protected-access
776
+ log_extra_keys(LOG_API, 'connection status', data, {'connection'})
777
+ return vehicle
778
+
713
779
  def fetch_parking_position(self, vehicle: SeatCupraVehicle, no_cache: bool = False) -> SeatCupraVehicle:
714
780
  """
715
781
  Fetches the position of the given vehicle and updates its position attributes.
@@ -1,12 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to this project will be documented in this file.
4
-
5
- ## [Unreleased]
6
- - No unreleased changes so far
7
-
8
- ## [0.1] - XXXX-XX-XX
9
- Initial release, let's go and give this to the public to try out...
10
-
11
- [unreleased]: https://github.com/tillsteinbach/CarConnectivity-connector-seatcupra/compare/v0.1...HEAD
12
- [0.1]: https://github.com/tillsteinbach/CarConnectivity-connector-seatcupra/releases/tag/v0.1