carconnectivity-connector-seatcupra 0.1a17__py3-none-any.whl → 0.1.1__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.1a17.dist-info → carconnectivity_connector_seatcupra-0.1.1.dist-info}/METADATA +2 -5
- {carconnectivity_connector_seatcupra-0.1a17.dist-info → carconnectivity_connector_seatcupra-0.1.1.dist-info}/RECORD +8 -8
- carconnectivity_connectors/seatcupra/_version.py +2 -2
- carconnectivity_connectors/seatcupra/capability.py +11 -4
- carconnectivity_connectors/seatcupra/connector.py +84 -9
- {carconnectivity_connector_seatcupra-0.1a17.dist-info → carconnectivity_connector_seatcupra-0.1.1.dist-info}/LICENSE +0 -0
- {carconnectivity_connector_seatcupra-0.1a17.dist-info → carconnectivity_connector_seatcupra-0.1.1.dist-info}/WHEEL +0 -0
- {carconnectivity_connector_seatcupra-0.1a17.dist-info → carconnectivity_connector_seatcupra-0.1.1.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.1.1
|
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.
|
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
|
[](https://www.paypal.com/donate?hosted_button_id=2BVFF5GJ9SXAJ)
|
55
55
|
[](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,10 +1,10 @@
|
|
1
1
|
carconnectivity_connectors/seatcupra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
carconnectivity_connectors/seatcupra/_version.py,sha256=
|
3
|
-
carconnectivity_connectors/seatcupra/capability.py,sha256=
|
2
|
+
carconnectivity_connectors/seatcupra/_version.py,sha256=Mmxse1R0ki5tjz9qzU8AQyqUsLt8nTyCAbYQp8R87PU,511
|
3
|
+
carconnectivity_connectors/seatcupra/capability.py,sha256=936V06hOX8AuAMxL_S9wVyVa36Xw1bo9081X0xf5f94,5064
|
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=LmBOCWGZPfJCG_4-5449xvO6NAvnPDsAWEBKlsG4WoI,3051
|
7
|
-
carconnectivity_connectors/seatcupra/connector.py,sha256=
|
7
|
+
carconnectivity_connectors/seatcupra/connector.py,sha256=llxFhVdpnL5MxmHbOej1wMio5tareY3zha0qaFhnPgs,107680
|
8
8
|
carconnectivity_connectors/seatcupra/vehicle.py,sha256=s0G-HqG5qcwStDxD3649KgLMa3lKPZ4TOGWRJEuQzsQ,3403
|
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
|
@@ -14,8 +14,8 @@ carconnectivity_connectors/seatcupra/auth/session_manager.py,sha256=ZIDvC848T3fy
|
|
14
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.1.1.dist-info/LICENSE,sha256=PIwI1alwDyOfvEQHdGCm2u9uf_mGE8030xZDfun0xTo,1071
|
18
|
+
carconnectivity_connector_seatcupra-0.1.1.dist-info/METADATA,sha256=Vh1baAcurqTyzfkXNO_Ts4GNO8nbFwXgpDS3r8kK2xs,5471
|
19
|
+
carconnectivity_connector_seatcupra-0.1.1.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
20
|
+
carconnectivity_connector_seatcupra-0.1.1.dist-info/top_level.txt,sha256=KqA8GviZsDH4PtmnwSQsz0HB_w-TWkeEHLIRNo5dTaI,27
|
21
|
+
carconnectivity_connector_seatcupra-0.1.1.dist-info/RECORD,,
|
@@ -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
|
-
|
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.
|
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
|
@@ -191,25 +191,34 @@ class Connector(BaseConnector):
|
|
191
191
|
raise
|
192
192
|
except TooManyRequestsError as err:
|
193
193
|
LOG.error('Retrieval error during update. Too many requests from your account (%s). Will try again after 15 minutes', str(err))
|
194
|
+
self.connection_state._set_value(value=ConnectionState.ERROR) # pylint: disable=protected-access
|
194
195
|
self._stop_event.wait(900)
|
195
196
|
except RetrievalError as err:
|
196
197
|
LOG.error('Retrieval error during update (%s). Will try again after configured interval of %ss', str(err), interval)
|
198
|
+
self.connection_state._set_value(value=ConnectionState.ERROR) # pylint: disable=protected-access
|
197
199
|
self._stop_event.wait(interval)
|
198
200
|
except APIError as err:
|
199
201
|
LOG.error('API error during update (%s). Will try again after configured interval of %ss', str(err), interval)
|
202
|
+
self.connection_state._set_value(value=ConnectionState.ERROR) # pylint: disable=protected-access
|
200
203
|
self._stop_event.wait(interval)
|
201
204
|
except APICompatibilityError as err:
|
202
205
|
LOG.error('API compatability error during update (%s). Will try again after configured interval of %ss', str(err), interval)
|
206
|
+
self.connection_state._set_value(value=ConnectionState.ERROR) # pylint: disable=protected-access
|
203
207
|
self._stop_event.wait(interval)
|
204
208
|
except TemporaryAuthenticationError as err:
|
205
209
|
LOG.error('Temporary authentification error during update (%s). Will try again after configured interval of %ss', str(err), interval)
|
210
|
+
self.connection_state._set_value(value=ConnectionState.ERROR) # pylint: disable=protected-access
|
206
211
|
self._stop_event.wait(interval)
|
207
212
|
except Exception as err:
|
208
213
|
LOG.critical('Critical error during update: %s', traceback.format_exc())
|
214
|
+
self.connection_state._set_value(value=ConnectionState.ERROR) # pylint: disable=protected-access
|
209
215
|
self.healthy._set_value(value=False) # pylint: disable=protected-access
|
210
216
|
raise err
|
211
217
|
else:
|
218
|
+
self.connection_state._set_value(value=ConnectionState.CONNECTED) # pylint: disable=protected-access
|
212
219
|
self._stop_event.wait(interval)
|
220
|
+
# When leaving the loop, set the connection state to disconnected
|
221
|
+
self.connection_state._set_value(value=ConnectionState.DISCONNECTED) # pylint: disable=protected-access
|
213
222
|
|
214
223
|
def persist(self) -> None:
|
215
224
|
"""
|
@@ -279,16 +288,32 @@ class Connector(BaseConnector):
|
|
279
288
|
vehicle_to_update = self.fetch_vehicle_status(vehicle_to_update)
|
280
289
|
vehicle_to_update = self.fetch_vehicle_mycar_status(vehicle_to_update)
|
281
290
|
vehicle_to_update = self.fetch_mileage(vehicle_to_update)
|
282
|
-
if vehicle_to_update.capabilities.has_capability('climatisation'):
|
291
|
+
if vehicle_to_update.capabilities.has_capability('climatisation', check_status_ok=True):
|
283
292
|
vehicle_to_update = self.fetch_climatisation(vehicle_to_update)
|
284
|
-
if vehicle_to_update.capabilities.has_capability('charging'):
|
293
|
+
if vehicle_to_update.capabilities.has_capability('charging', check_status_ok=True):
|
285
294
|
vehicle_to_update = self.fetch_charging(vehicle_to_update)
|
286
|
-
if vehicle_to_update.capabilities.has_capability('parkingPosition'):
|
295
|
+
if vehicle_to_update.capabilities.has_capability('parkingPosition', check_status_ok=True):
|
287
296
|
vehicle_to_update = self.fetch_parking_position(vehicle_to_update)
|
288
|
-
if vehicle_to_update.capabilities.has_capability('vehicleHealthInspection'):
|
297
|
+
if vehicle_to_update.capabilities.has_capability('vehicleHealthInspection', check_status_ok=True):
|
289
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)
|
290
301
|
self.car_connectivity.transaction_end()
|
291
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
|
+
|
292
317
|
def fetch_vehicles(self) -> None:
|
293
318
|
"""
|
294
319
|
Fetches the list of vehicles from the Seat/Cupra Connect API and updates the garage with new vehicles.
|
@@ -360,6 +385,19 @@ class Connector(BaseConnector):
|
|
360
385
|
else:
|
361
386
|
capability = Capability(capability_id=capability_id, capabilities=vehicle.capabilities)
|
362
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()
|
363
401
|
if 'expirationDate' in capability_dict and capability_dict['expirationDate'] is not None \
|
364
402
|
and capability_dict['expirationDate'] != '':
|
365
403
|
expiration_date: datetime = robust_time_parse(capability_dict['expirationDate'])
|
@@ -381,7 +419,7 @@ class Connector(BaseConnector):
|
|
381
419
|
for capability_id in vehicle.capabilities.capabilities.keys() - found_capabilities:
|
382
420
|
vehicle.capabilities.remove_capability(capability_id)
|
383
421
|
|
384
|
-
if vehicle.capabilities.has_capability('charging'):
|
422
|
+
if vehicle.capabilities.has_capability('charging', check_status_ok=True):
|
385
423
|
if not isinstance(vehicle, SeatCupraElectricVehicle):
|
386
424
|
LOG.debug('Promoting %s to SeatCupraElectricVehicle object for %s', vehicle.__class__.__name__, vin)
|
387
425
|
vehicle = SeatCupraElectricVehicle(origin=vehicle)
|
@@ -392,7 +430,7 @@ class Connector(BaseConnector):
|
|
392
430
|
charging_start_stop_command.enabled = True
|
393
431
|
vehicle.charging.commands.add_command(charging_start_stop_command)
|
394
432
|
|
395
|
-
if vehicle.capabilities.has_capability('climatisation'):
|
433
|
+
if vehicle.capabilities.has_capability('climatisation', check_status_ok=True):
|
396
434
|
if vehicle.climatization is not None and vehicle.climatization.commands is not None \
|
397
435
|
and not vehicle.climatization.commands.contains_command('start-stop'):
|
398
436
|
climatisation_start_stop_command: ClimatizationStartStopCommand = \
|
@@ -402,7 +440,7 @@ class Connector(BaseConnector):
|
|
402
440
|
climatisation_start_stop_command.enabled = True
|
403
441
|
vehicle.climatization.commands.add_command(climatisation_start_stop_command)
|
404
442
|
|
405
|
-
if vehicle.capabilities.has_capability('vehicleWakeUpTrigger'):
|
443
|
+
if vehicle.capabilities.has_capability('vehicleWakeUpTrigger', check_status_ok=True):
|
406
444
|
if vehicle.commands is not None and vehicle.commands.commands is not None \
|
407
445
|
and not vehicle.commands.contains_command('wake-sleep'):
|
408
446
|
wake_sleep_command = WakeSleepCommand(parent=vehicle.commands)
|
@@ -411,7 +449,7 @@ class Connector(BaseConnector):
|
|
411
449
|
vehicle.commands.add_command(wake_sleep_command)
|
412
450
|
|
413
451
|
# Add honkAndFlash command if necessary capabilities are available
|
414
|
-
if vehicle.capabilities.has_capability('honkAndFlash'):
|
452
|
+
if vehicle.capabilities.has_capability('honkAndFlash', check_status_ok=True):
|
415
453
|
if vehicle.commands is not None and vehicle.commands.commands is not None \
|
416
454
|
and not vehicle.commands.contains_command('honk-flash'):
|
417
455
|
honk_flash_command = HonkAndFlashCommand(parent=vehicle.commands, with_duration=True)
|
@@ -420,7 +458,7 @@ class Connector(BaseConnector):
|
|
420
458
|
vehicle.commands.add_command(honk_flash_command)
|
421
459
|
|
422
460
|
# Add lock and unlock command
|
423
|
-
if vehicle.capabilities.has_capability('access'):
|
461
|
+
if vehicle.capabilities.has_capability('access', check_status_ok=True):
|
424
462
|
if vehicle.doors is not None and vehicle.doors.commands is not None and vehicle.doors.commands.commands is not None \
|
425
463
|
and not vehicle.doors.commands.contains_command('lock-unlock'):
|
426
464
|
lock_unlock_command = LockUnlockCommand(parent=vehicle.doors.commands)
|
@@ -701,6 +739,43 @@ class Connector(BaseConnector):
|
|
701
739
|
'remainingTime'})
|
702
740
|
return vehicle
|
703
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
|
+
|
704
779
|
def fetch_parking_position(self, vehicle: SeatCupraVehicle, no_cache: bool = False) -> SeatCupraVehicle:
|
705
780
|
"""
|
706
781
|
Fetches the position of the given vehicle and updates its position attributes.
|
File without changes
|
File without changes
|