carconnectivity-connector-seatcupra 0.1a2__tar.gz → 0.1a4__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.
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/PKG-INFO +2 -2
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/pyproject.toml +1 -1
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connector_seatcupra.egg-info/PKG-INFO +2 -2
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connector_seatcupra.egg-info/SOURCES.txt +2 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connector_seatcupra.egg-info/requires.txt +1 -1
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connectors/seatcupra/_version.py +1 -1
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connectors/seatcupra/charging.py +1 -1
- carconnectivity_connector_seatcupra-0.1a4/src/carconnectivity_connectors/seatcupra/climatization.py +39 -0
- carconnectivity_connector_seatcupra-0.1a4/src/carconnectivity_connectors/seatcupra/command_impl.py +72 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connectors/seatcupra/connector.py +413 -65
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connectors/seatcupra/vehicle.py +3 -1
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/.flake8 +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/.github/dependabot.yml +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/.github/workflows/build.yml +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/.github/workflows/build_and_publish.yml +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/.github/workflows/codeql-analysis.yml +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/.gitignore +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/CHANGELOG.md +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/LICENSE +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/Makefile +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/README.md +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/doc/Config.md +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/setup.cfg +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/setup_requirements.txt +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connector_seatcupra.egg-info/dependency_links.txt +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connector_seatcupra.egg-info/top_level.txt +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connectors/seatcupra/__init__.py +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connectors/seatcupra/auth/__init__.py +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connectors/seatcupra/auth/auth_util.py +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connectors/seatcupra/auth/helpers/blacklist_retry.py +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connectors/seatcupra/auth/my_cupra_session.py +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connectors/seatcupra/auth/openid_session.py +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connectors/seatcupra/auth/session_manager.py +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connectors/seatcupra/auth/vw_web_session.py +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connectors/seatcupra/capability.py +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/src/carconnectivity_connectors/seatcupra/ui/connector_ui.py +0 -0
- {carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/test/integration_test/carConnectivity.json +0 -0
{carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/PKG-INFO
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: carconnectivity-connector-seatcupra
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.1a4
|
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.4a2
|
41
41
|
Requires-Dist: oauthlib~=3.2.2
|
42
42
|
Requires-Dist: requests~=2.32.3
|
43
43
|
Requires-Dist: jwt~=1.3.1
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: carconnectivity-connector-seatcupra
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.1a4
|
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.4a2
|
41
41
|
Requires-Dist: oauthlib~=3.2.2
|
42
42
|
Requires-Dist: requests~=2.32.3
|
43
43
|
Requires-Dist: jwt~=1.3.1
|
@@ -22,6 +22,8 @@ src/carconnectivity_connectors/seatcupra/__init__.py
|
|
22
22
|
src/carconnectivity_connectors/seatcupra/_version.py
|
23
23
|
src/carconnectivity_connectors/seatcupra/capability.py
|
24
24
|
src/carconnectivity_connectors/seatcupra/charging.py
|
25
|
+
src/carconnectivity_connectors/seatcupra/climatization.py
|
26
|
+
src/carconnectivity_connectors/seatcupra/command_impl.py
|
25
27
|
src/carconnectivity_connectors/seatcupra/connector.py
|
26
28
|
src/carconnectivity_connectors/seatcupra/vehicle.py
|
27
29
|
src/carconnectivity_connectors/seatcupra/auth/__init__.py
|
@@ -32,7 +32,7 @@ class SeatCupraCharging(Charging): # pylint: disable=too-many-instance-attribut
|
|
32
32
|
"""
|
33
33
|
OFF = 'off'
|
34
34
|
READY_FOR_CHARGING = 'readyForCharging'
|
35
|
-
NOT_READY_FOR_CHARGING = '
|
35
|
+
NOT_READY_FOR_CHARGING = 'notReadyForCharging'
|
36
36
|
CONSERVATION = 'conservation'
|
37
37
|
CHARGE_PURPOSE_REACHED_NOT_CONSERVATION_CHARGING = 'chargePurposeReachedAndNotConservationCharging'
|
38
38
|
CHARGE_PURPOSE_REACHED_CONSERVATION = 'chargePurposeReachedAndConservation'
|
carconnectivity_connector_seatcupra-0.1a4/src/carconnectivity_connectors/seatcupra/climatization.py
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
"""
|
2
|
+
Module for charging for Seat/Cupra vehicles.
|
3
|
+
"""
|
4
|
+
from __future__ import annotations
|
5
|
+
from typing import TYPE_CHECKING
|
6
|
+
|
7
|
+
from carconnectivity.climatization import Climatization
|
8
|
+
from carconnectivity.objects import GenericObject
|
9
|
+
from carconnectivity.vehicle import GenericVehicle
|
10
|
+
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from typing import Optional
|
13
|
+
|
14
|
+
|
15
|
+
class SeatCupraClimatization(Climatization): # pylint: disable=too-many-instance-attributes
|
16
|
+
"""
|
17
|
+
SeatCupraClimatization class for handling Seat/Cupra vehicle climatization information.
|
18
|
+
|
19
|
+
This class extends the Climatization class and includes an enumeration of various
|
20
|
+
climatization states specific to Volkswagen vehicles.
|
21
|
+
"""
|
22
|
+
def __init__(self, vehicle: GenericVehicle | None = None, origin: Optional[Climatization] = None) -> None:
|
23
|
+
if origin is not None:
|
24
|
+
super().__init__(vehicle=vehicle, origin=origin)
|
25
|
+
if not isinstance(self.settings, SeatCupraClimatization.Settings):
|
26
|
+
self.settings: Climatization.Settings = SeatCupraClimatization.Settings(parent=self, origin=origin.settings)
|
27
|
+
else:
|
28
|
+
super().__init__(vehicle=vehicle)
|
29
|
+
self.settings: Climatization.Settings = SeatCupraClimatization.Settings(parent=self, origin=self.settings)
|
30
|
+
|
31
|
+
class Settings(Climatization.Settings):
|
32
|
+
"""
|
33
|
+
This class represents the settings for a skoda car climatiation.
|
34
|
+
"""
|
35
|
+
def __init__(self, parent: Optional[GenericObject] = None, origin: Optional[Climatization.Settings] = None) -> None:
|
36
|
+
if origin is not None:
|
37
|
+
super().__init__(parent=parent, origin=origin)
|
38
|
+
else:
|
39
|
+
super().__init__(parent=parent)
|
carconnectivity_connector_seatcupra-0.1a4/src/carconnectivity_connectors/seatcupra/command_impl.py
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
"""This module defines the classes that represent attributes in the CarConnectivity system."""
|
2
|
+
from __future__ import annotations
|
3
|
+
from typing import TYPE_CHECKING, Dict, Union
|
4
|
+
|
5
|
+
from enum import Enum
|
6
|
+
import argparse
|
7
|
+
import logging
|
8
|
+
|
9
|
+
from carconnectivity.commands import GenericCommand
|
10
|
+
from carconnectivity.objects import GenericObject
|
11
|
+
from carconnectivity.errors import SetterError
|
12
|
+
from carconnectivity.util import ThrowingArgumentParser
|
13
|
+
|
14
|
+
if TYPE_CHECKING:
|
15
|
+
from carconnectivity.objects import Optional
|
16
|
+
|
17
|
+
LOG: logging.Logger = logging.getLogger("carconnectivity.connectors.seatcupra")
|
18
|
+
|
19
|
+
|
20
|
+
class SpinCommand(GenericCommand):
|
21
|
+
"""
|
22
|
+
SpinCommand is a command class for verifying the spin
|
23
|
+
|
24
|
+
"""
|
25
|
+
def __init__(self, name: str = 'spin', parent: Optional[GenericObject] = None) -> None:
|
26
|
+
super().__init__(name=name, parent=parent)
|
27
|
+
|
28
|
+
@property
|
29
|
+
def value(self) -> Optional[Union[str, Dict]]:
|
30
|
+
return super().value
|
31
|
+
|
32
|
+
@value.setter
|
33
|
+
def value(self, new_value: Optional[Union[str, Dict]]) -> None:
|
34
|
+
if isinstance(new_value, str):
|
35
|
+
parser = ThrowingArgumentParser(prog='', add_help=False, exit_on_error=False)
|
36
|
+
parser.add_argument('command', help='Command to execute', type=SpinCommand.Command,
|
37
|
+
choices=list(SpinCommand.Command))
|
38
|
+
parser.add_argument('--spin', dest='spin', help='Spin to be used instead of spin from config or .netrc', type=str, required=False,
|
39
|
+
default=None)
|
40
|
+
try:
|
41
|
+
args = parser.parse_args(new_value.split(sep=' '))
|
42
|
+
except argparse.ArgumentError as e:
|
43
|
+
raise SetterError(f'Invalid format for SpinCommand: {e.message} {parser.format_usage()}') from e
|
44
|
+
|
45
|
+
newvalue_dict = {}
|
46
|
+
newvalue_dict['command'] = args.command
|
47
|
+
if args.spin is not None:
|
48
|
+
newvalue_dict['spin'] = args.spin
|
49
|
+
new_value = newvalue_dict
|
50
|
+
elif isinstance(new_value, dict):
|
51
|
+
if 'command' in new_value and isinstance(new_value['command'], str):
|
52
|
+
if new_value['command'] in SpinCommand.Command:
|
53
|
+
new_value['command'] = SpinCommand.Command(new_value['command'])
|
54
|
+
else:
|
55
|
+
raise ValueError('Invalid value for SpinCommand. '
|
56
|
+
f'Command must be one of {SpinCommand.Command}')
|
57
|
+
if self._is_changeable:
|
58
|
+
for hook in self._on_set_hooks:
|
59
|
+
new_value = hook(self, new_value)
|
60
|
+
self._set_value(new_value)
|
61
|
+
else:
|
62
|
+
raise TypeError('You cannot set this attribute. Attribute is not mutable.')
|
63
|
+
|
64
|
+
class Command(Enum):
|
65
|
+
"""
|
66
|
+
Enum class representing different commands for SPIN.
|
67
|
+
|
68
|
+
"""
|
69
|
+
VERIFY = 'verify'
|
70
|
+
|
71
|
+
def __str__(self) -> str:
|
72
|
+
return self.value
|
@@ -15,7 +15,7 @@ from carconnectivity.garage import Garage
|
|
15
15
|
from carconnectivity.errors import AuthenticationError, TooManyRequestsError, RetrievalError, APIError, APICompatibilityError, \
|
16
16
|
TemporaryAuthenticationError, SetterError, CommandError
|
17
17
|
from carconnectivity.util import robust_time_parse, log_extra_keys, config_remove_credentials
|
18
|
-
from carconnectivity.units import Length,
|
18
|
+
from carconnectivity.units import Length, Current
|
19
19
|
from carconnectivity.doors import Doors
|
20
20
|
from carconnectivity.windows import Windows
|
21
21
|
from carconnectivity.lights import Lights
|
@@ -27,6 +27,7 @@ from carconnectivity.command_impl import ClimatizationStartStopCommand, WakeSlee
|
|
27
27
|
from carconnectivity.climatization import Climatization
|
28
28
|
from carconnectivity.commands import Commands
|
29
29
|
from carconnectivity.charging import Charging
|
30
|
+
from carconnectivity.charging_connector import ChargingConnector
|
30
31
|
from carconnectivity.position import Position
|
31
32
|
|
32
33
|
from carconnectivity_connectors.base.connector import BaseConnector
|
@@ -36,6 +37,8 @@ from carconnectivity_connectors.seatcupra._version import __version__
|
|
36
37
|
from carconnectivity_connectors.seatcupra.capability import Capability
|
37
38
|
from carconnectivity_connectors.seatcupra.vehicle import SeatCupraVehicle, SeatCupraElectricVehicle, SeatCupraCombustionVehicle, SeatCupraHybridVehicle
|
38
39
|
from carconnectivity_connectors.seatcupra.charging import SeatCupraCharging, mapping_seatcupra_charging_state
|
40
|
+
from carconnectivity_connectors.seatcupra.climatization import SeatCupraClimatization
|
41
|
+
from carconnectivity_connectors.seatcupra.command_impl import SpinCommand
|
39
42
|
|
40
43
|
SUPPORT_IMAGES = False
|
41
44
|
try:
|
@@ -222,6 +225,12 @@ class Connector(BaseConnector):
|
|
222
225
|
|
223
226
|
This method calls the `fetch_vehicles` method to retrieve vehicle data.
|
224
227
|
"""
|
228
|
+
# Add spin command
|
229
|
+
if self.commands is not None and not self.commands.contains_command('spin'):
|
230
|
+
spin_command = SpinCommand(parent=self.commands)
|
231
|
+
spin_command._add_on_set_hook(self.__on_spin) # pylint: disable=protected-access
|
232
|
+
spin_command.enabled = True
|
233
|
+
self.commands.add_command(spin_command)
|
225
234
|
self.fetch_vehicles()
|
226
235
|
self.car_connectivity.transaction_end()
|
227
236
|
|
@@ -339,10 +348,10 @@ class Connector(BaseConnector):
|
|
339
348
|
else:
|
340
349
|
raise APIError('Could not fetch capabilities, capability ID missing')
|
341
350
|
log_extra_keys(LOG_API, 'capability', capability_dict, {'id', 'expirationDate', 'editable', 'parameters'})
|
342
|
-
|
351
|
+
|
343
352
|
for capability_id in vehicle.capabilities.capabilities.keys() - found_capabilities:
|
344
353
|
vehicle.capabilities.remove_capability(capability_id)
|
345
|
-
|
354
|
+
|
346
355
|
if vehicle.capabilities.has_capability('charging'):
|
347
356
|
if not isinstance(vehicle, SeatCupraElectricVehicle):
|
348
357
|
LOG.debug('Promoting %s to SeatCupraElectricVehicle object for %s', vehicle.__class__.__name__, vin)
|
@@ -353,7 +362,7 @@ class Connector(BaseConnector):
|
|
353
362
|
charging_start_stop_command._add_on_set_hook(self.__on_charging_start_stop) # pylint: disable=protected-access
|
354
363
|
charging_start_stop_command.enabled = True
|
355
364
|
vehicle.charging.commands.add_command(charging_start_stop_command)
|
356
|
-
|
365
|
+
|
357
366
|
if vehicle.capabilities.has_capability('climatisation'):
|
358
367
|
if vehicle.climatization is not None and vehicle.climatization.commands is not None \
|
359
368
|
and not vehicle.climatization.commands.contains_command('start-stop'):
|
@@ -363,8 +372,36 @@ class Connector(BaseConnector):
|
|
363
372
|
climatisation_start_stop_command._add_on_set_hook(self.__on_air_conditioning_start_stop)
|
364
373
|
climatisation_start_stop_command.enabled = True
|
365
374
|
vehicle.climatization.commands.add_command(climatisation_start_stop_command)
|
366
|
-
|
367
|
-
vehicle.capabilities.
|
375
|
+
|
376
|
+
if vehicle.capabilities.has_capability('vehicleWakeUpTrigger'):
|
377
|
+
if vehicle.commands is not None and vehicle.commands.commands is not None \
|
378
|
+
and not vehicle.commands.contains_command('wake-sleep'):
|
379
|
+
wake_sleep_command = WakeSleepCommand(parent=vehicle.commands)
|
380
|
+
wake_sleep_command._add_on_set_hook(self.__on_wake_sleep) # pylint: disable=protected-access
|
381
|
+
wake_sleep_command.enabled = True
|
382
|
+
vehicle.commands.add_command(wake_sleep_command)
|
383
|
+
|
384
|
+
# Add honkAndFlash command if necessary capabilities are available
|
385
|
+
if vehicle.capabilities.has_capability('honkAndFlash'):
|
386
|
+
if vehicle.commands is not None and vehicle.commands.commands is not None \
|
387
|
+
and not vehicle.commands.contains_command('honk-flash'):
|
388
|
+
honk_flash_command = HonkAndFlashCommand(parent=vehicle.commands, with_duration=True)
|
389
|
+
honk_flash_command._add_on_set_hook(self.__on_honk_flash) # pylint: disable=protected-access
|
390
|
+
honk_flash_command.enabled = True
|
391
|
+
vehicle.commands.add_command(honk_flash_command)
|
392
|
+
|
393
|
+
# Add lock and unlock command
|
394
|
+
if vehicle.capabilities.has_capability('access'):
|
395
|
+
if vehicle.doors is not None and vehicle.doors.commands is not None and vehicle.doors.commands.commands is not None \
|
396
|
+
and not vehicle.doors.commands.contains_command('lock-unlock'):
|
397
|
+
lock_unlock_command = LockUnlockCommand(parent=vehicle.doors.commands)
|
398
|
+
lock_unlock_command._add_on_set_hook(self.__on_lock_unlock) # pylint: disable=protected-access
|
399
|
+
lock_unlock_command.enabled = True
|
400
|
+
vehicle.doors.commands.add_command(lock_unlock_command)
|
401
|
+
else:
|
402
|
+
vehicle.capabilities.clear_capabilities()
|
403
|
+
if isinstance(vehicle, SeatCupraVehicle):
|
404
|
+
vehicle = self.fetch_image(vehicle)
|
368
405
|
else:
|
369
406
|
raise APIError('Could not fetch vehicle data, VIN missing')
|
370
407
|
for vin in set(garage.list_vehicle_vins()) - seen_vehicle_vins:
|
@@ -386,7 +423,7 @@ class Connector(BaseConnector):
|
|
386
423
|
vin = vehicle.vin.value
|
387
424
|
if vin is None:
|
388
425
|
raise APIError('VIN is missing')
|
389
|
-
|
426
|
+
|
390
427
|
url = f'https://ola.prod.code.seat.cloud.vwgroup.com/vehicles/{vin}/connection'
|
391
428
|
vehicle_connection_data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
392
429
|
if vehicle_connection_data is not None:
|
@@ -514,11 +551,10 @@ class Connector(BaseConnector):
|
|
514
551
|
vin = vehicle.vin.value
|
515
552
|
if vin is None:
|
516
553
|
raise APIError('VIN is missing')
|
517
|
-
url = f'https://ola.prod.code.seat.cloud.vwgroup.com/v1/vehicles/{vin}/measurements/engines'
|
518
|
-
vehicle_status_data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
519
|
-
#measurements
|
520
|
-
#{'primary': {'fuelType': 'gasoline', 'rangeInKm': 120.0}, 'secondary': {'fuelType': 'electric', 'rangeInKm': 40.0}}
|
521
|
-
print(vehicle_status_data)
|
554
|
+
# url = f'https://ola.prod.code.seat.cloud.vwgroup.com/v1/vehicles/{vin}/measurements/engines'
|
555
|
+
# vehicle_status_data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
556
|
+
# measurements
|
557
|
+
# {'primary': {'fuelType': 'gasoline', 'rangeInKm': 120.0}, 'secondary': {'fuelType': 'electric', 'rangeInKm': 40.0}}
|
522
558
|
url = f'https://ola.prod.code.seat.cloud.vwgroup.com/v5/users/{self.session.user_id}/vehicles/{vin}/mycar'
|
523
559
|
vehicle_status_data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
524
560
|
if vehicle_status_data:
|
@@ -590,23 +626,6 @@ class Connector(BaseConnector):
|
|
590
626
|
if 'services' in vehicle_status_data and vehicle_status_data['services'] is not None:
|
591
627
|
if 'charging' in vehicle_status_data['services'] and vehicle_status_data['services']['charging'] is not None:
|
592
628
|
charging_status: Dict = vehicle_status_data['services']['charging']
|
593
|
-
if 'status' in charging_status and charging_status['status'] is not None:
|
594
|
-
if charging_status['status'] in SeatCupraCharging.SeatCupraChargingState:
|
595
|
-
volkswagen_charging_state = SeatCupraCharging.SeatCupraChargingState(charging_status['status'])
|
596
|
-
charging_state: Charging.ChargingState = mapping_seatcupra_charging_state[volkswagen_charging_state]
|
597
|
-
else:
|
598
|
-
LOG_API.info('Unkown charging state %s not in %s', charging_status['status'],
|
599
|
-
str(SeatCupraCharging.SeatCupraChargingState))
|
600
|
-
charging_state = Charging.ChargingState.UNKNOWN
|
601
|
-
if isinstance(vehicle, ElectricVehicle):
|
602
|
-
vehicle.charging.state._set_value(value=charging_state) # pylint: disable=protected-access
|
603
|
-
else:
|
604
|
-
LOG_API.warning('Vehicle is not an electric or hybrid vehicle, but charging state was fetched')
|
605
|
-
else:
|
606
|
-
if isinstance(vehicle, ElectricVehicle):
|
607
|
-
vehicle.charging.state._set_value(None) # pylint: disable=protected-access
|
608
|
-
else:
|
609
|
-
LOG_API.warning('Vehicle is not an electric or hybrid vehicle, but charging state was fetched')
|
610
629
|
if 'targetPct' in charging_status and charging_status['targetPct'] is not None:
|
611
630
|
if isinstance(vehicle, ElectricVehicle):
|
612
631
|
vehicle.charging.settings.target_level._set_value(charging_status['targetPct']) # pylint: disable=protected-access
|
@@ -636,26 +655,7 @@ class Connector(BaseConnector):
|
|
636
655
|
vehicle.charging.enabled = False
|
637
656
|
if 'climatisation' in vehicle_status_data['services'] and vehicle_status_data['services']['climatisation'] is not None:
|
638
657
|
climatisation_status: Dict = vehicle_status_data['services']['climatisation']
|
639
|
-
|
640
|
-
if climatisation_status['status'].lower() in [item.value for item in Climatization.ClimatizationState]:
|
641
|
-
climatization_state: Climatization.ClimatizationState = Climatization.ClimatizationState(climatisation_status['status'].lower())
|
642
|
-
else:
|
643
|
-
LOG_API.info('Unknown climatization state %s not in %s', climatisation_status['status'],
|
644
|
-
str(Climatization.ClimatizationState))
|
645
|
-
climatization_state = Climatization.ClimatizationState.UNKNOWN
|
646
|
-
vehicle.climatization.state._set_value(value=climatization_state) # pylint: disable=protected-access
|
647
|
-
else:
|
648
|
-
vehicle.climatization.state._set_value(None) # pylint: disable=protected-access
|
649
|
-
if 'targetTemperatureCelsius' in climatisation_status and climatisation_status['targetTemperatureCelsius'] is not None:
|
650
|
-
target_temperature: Optional[float] = climatisation_status['targetTemperatureCelsius']
|
651
|
-
vehicle.climatization.settings.target_temperature._set_value(value=target_temperature, # pylint: disable=protected-access
|
652
|
-
unit=Temperature.C)
|
653
|
-
elif 'targetTemperatureFahrenheit' in climatisation_status and climatisation_status['targetTemperatureFahrenheit'] is not None:
|
654
|
-
target_temperature = climatisation_status['targetTemperatureFahrenheit']
|
655
|
-
vehicle.climatization.settings.target_temperature._set_value(value=target_temperature, # pylint: disable=protected-access
|
656
|
-
unit=Temperature.F)
|
657
|
-
else:
|
658
|
-
vehicle.climatization.settings.target_temperature._set_value(None) # pylint: disable=protected-access
|
658
|
+
|
659
659
|
if 'remainingTime' in climatisation_status and climatisation_status['remainingTime'] is not None:
|
660
660
|
remaining_duration: timedelta = timedelta(minutes=climatisation_status['remainingTime'])
|
661
661
|
estimated_date_reached: datetime = datetime.now(tz=timezone.utc) + remaining_duration
|
@@ -663,6 +663,7 @@ class Connector(BaseConnector):
|
|
663
663
|
vehicle.climatization.estimated_date_reached._set_value(value=estimated_date_reached) # pylint: disable=protected-access
|
664
664
|
else:
|
665
665
|
vehicle.climatization.estimated_date_reached._set_value(None) # pylint: disable=protected-access
|
666
|
+
# we take status, targetTemperatureCelsius, targetTemperatureFahrenheit, from climatization request
|
666
667
|
log_extra_keys(LOG_API, 'climatisation', climatisation_status, {'status', 'targetTemperatureCelsius', 'targetTemperatureFahrenheit',
|
667
668
|
'remainingTime'})
|
668
669
|
return vehicle
|
@@ -755,12 +756,68 @@ class Connector(BaseConnector):
|
|
755
756
|
raise APIError('VIN is missing')
|
756
757
|
url = f'https://ola.prod.code.seat.cloud.vwgroup.com/v1/vehicles/{vin}/climatisation/status'
|
757
758
|
data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
758
|
-
#{'climatisationStatus': {'carCapturedTimestamp': '2025-02-18T17:24:02Z', 'climatisationState': 'off', 'climatisationTrigger': 'unsupported'}, 'windowHeatingStatus': {'carCapturedTimestamp': '2025-02-18T16:57:51Z', 'windowHeatingStatus': [{'windowLocation': 'front', 'windowHeatingState': 'off'}, {'windowLocation': 'rear', 'windowHeatingState': 'off'}]}}
|
759
|
-
|
759
|
+
# {'climatisationStatus': {'carCapturedTimestamp': '2025-02-18T17:24:02Z', 'climatisationState': 'off', 'climatisationTrigger': 'unsupported'}, 'windowHeatingStatus': {'carCapturedTimestamp': '2025-02-18T16:57:51Z', 'windowHeatingStatus': [{'windowLocation': 'front', 'windowHeatingState': 'off'}, {'windowLocation': 'rear', 'windowHeatingState': 'off'}]}}
|
760
|
+
if data is not None:
|
761
|
+
if 'climatisationStatus' in data and data['climatisationStatus'] is not None:
|
762
|
+
climatisation_status: Dict = data['climatisationStatus']
|
763
|
+
if 'carCapturedTimestamp' not in climatisation_status or climatisation_status['carCapturedTimestamp'] is None:
|
764
|
+
raise APIError('Could not fetch vehicle status, carCapturedTimestamp missing')
|
765
|
+
captured_at: datetime = robust_time_parse(climatisation_status['carCapturedTimestamp'])
|
766
|
+
if 'climatisationState' in climatisation_status and climatisation_status['climatisationState'] is not None:
|
767
|
+
if climatisation_status['climatisationState'].lower() in [item.value for item in Climatization.ClimatizationState]:
|
768
|
+
climatization_state: Climatization.ClimatizationState = \
|
769
|
+
Climatization.ClimatizationState(climatisation_status['climatisationState'].lower())
|
770
|
+
else:
|
771
|
+
LOG_API.info('Unknown climatization state %s not in %s', climatisation_status['climatisationState'],
|
772
|
+
str(Climatization.ClimatizationState))
|
773
|
+
climatization_state = Climatization.ClimatizationState.UNKNOWN
|
774
|
+
vehicle.climatization.state._set_value(value=climatization_state, measured=captured_at) # pylint: disable=protected-access
|
775
|
+
else:
|
776
|
+
vehicle.climatization.state._set_value(None) # pylint: disable=protected-access
|
777
|
+
log_extra_keys(LOG_API, 'climatisation', data, {'carCapturedTimestamp', 'climatisationState'})
|
778
|
+
else:
|
779
|
+
vehicle.climatization.state._set_value(None) # pylint: disable=protected-access
|
780
|
+
log_extra_keys(LOG_API, 'climatisation', data, {'climatisationStatus'})
|
760
781
|
url = f'https://ola.prod.code.seat.cloud.vwgroup.com/v2/vehicles/{vin}/climatisation/settings'
|
761
782
|
data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
762
|
-
|
763
|
-
|
783
|
+
if data is not None:
|
784
|
+
if not isinstance(vehicle.climatization, SeatCupraClimatization):
|
785
|
+
vehicle.climatization = SeatCupraClimatization(vehicle=vehicle, origin=vehicle.climatization)
|
786
|
+
if 'carCapturedTimestamp' not in data or data['carCapturedTimestamp'] is None:
|
787
|
+
raise APIError('Could not fetch vehicle status, carCapturedTimestamp missing')
|
788
|
+
captured_at: datetime = robust_time_parse(data['carCapturedTimestamp'])
|
789
|
+
if 'targetTemperatureInCelsius' in data and data['targetTemperatureInCelsius'] is not None:
|
790
|
+
# pylint: disable-next=protected-access
|
791
|
+
vehicle.climatization.settings.target_temperature._add_on_set_hook(self.__on_air_conditioning_settings_change)
|
792
|
+
vehicle.climatization.settings.target_temperature._is_changeable = True # pylint: disable=protected-access
|
793
|
+
|
794
|
+
target_temperature: Optional[float] = data['targetTemperatureInCelsius']
|
795
|
+
vehicle.climatization.settings.target_temperature._set_value(value=target_temperature, # pylint: disable=protected-access
|
796
|
+
measured=captured_at,
|
797
|
+
unit=Temperature.C)
|
798
|
+
elif 'targetTemperatureInFahrenheit' in data and data['targetTemperatureInFahrenheit'] is not None:
|
799
|
+
# pylint: disable-next=protected-access
|
800
|
+
vehicle.climatization.settings.target_temperature._add_on_set_hook(self.__on_air_conditioning_settings_change)
|
801
|
+
vehicle.climatization.settings.target_temperature._is_changeable = True # pylint: disable=protected-access
|
802
|
+
|
803
|
+
target_temperature = data['targetTemperatureInFahrenheit']
|
804
|
+
vehicle.climatization.settings.target_temperature._set_value(value=target_temperature, # pylint: disable=protected-access
|
805
|
+
measured=captured_at,
|
806
|
+
unit=Temperature.F)
|
807
|
+
else:
|
808
|
+
vehicle.climatization.settings.target_temperature._set_value(None) # pylint: disable=protected-access
|
809
|
+
if 'climatisationWithoutExternalPower' in data and data['climatisationWithoutExternalPower'] is not None:
|
810
|
+
# pylint: disable-next=protected-access
|
811
|
+
vehicle.climatization.settings.climatization_without_external_power._add_on_set_hook(self.__on_air_conditioning_settings_change)
|
812
|
+
vehicle.climatization.settings.climatization_without_external_power._is_changeable = True # pylint: disable=protected-access
|
813
|
+
|
814
|
+
# pylint: disable-next=protected-access
|
815
|
+
vehicle.climatization.settings.climatization_without_external_power._set_value(data['climatisationWithoutExternalPower'],
|
816
|
+
measured=captured_at)
|
817
|
+
else:
|
818
|
+
vehicle.climatization.settings.climatization_without_external_power._set_value(None) # pylint: disable=protected-access
|
819
|
+
log_extra_keys(LOG_API, f'https://ola.prod.code.seat.cloud.vwgroup.com/v2/vehicles/{vin}/climatisation/settings', data,
|
820
|
+
{'carCapturedTimestamp', 'targetTemperatureInCelsius', 'targetTemperatureInFahrenheit', 'climatisationWithoutExternalPower'})
|
764
821
|
return vehicle
|
765
822
|
|
766
823
|
def fetch_charging(self, vehicle: SeatCupraVehicle, no_cache: bool = False) -> SeatCupraVehicle:
|
@@ -780,18 +837,147 @@ class Connector(BaseConnector):
|
|
780
837
|
vin = vehicle.vin.value
|
781
838
|
if vin is None:
|
782
839
|
raise APIError('VIN is missing')
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
840
|
+
if isinstance(vehicle, ElectricVehicle):
|
841
|
+
url = f'https://ola.prod.code.seat.cloud.vwgroup.com/v1/vehicles/{vin}/charging/status'
|
842
|
+
data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
843
|
+
|
844
|
+
if data is not None:
|
845
|
+
if 'charging' in data and data['charging'] is not None:
|
846
|
+
if 'state' in data['charging'] and data['charging']['state'] is not None:
|
847
|
+
if data['charging']['state'] in SeatCupraCharging.SeatCupraChargingState:
|
848
|
+
volkswagen_charging_state = SeatCupraCharging.SeatCupraChargingState(data['charging']['state'])
|
849
|
+
charging_state: Charging.ChargingState = mapping_seatcupra_charging_state[volkswagen_charging_state]
|
850
|
+
else:
|
851
|
+
LOG_API.info('Unkown charging state %s not in %s', data['charging']['state'],
|
852
|
+
str(SeatCupraCharging.SeatCupraChargingState))
|
853
|
+
charging_state = Charging.ChargingState.UNKNOWN
|
854
|
+
vehicle.charging.state._set_value(value=charging_state) # pylint: disable=protected-access
|
855
|
+
else:
|
856
|
+
vehicle.charging.state._set_value(None) # pylint: disable=protected-access
|
857
|
+
log_extra_keys(LOG_API, 'charging', data['charging'], {'state'})
|
858
|
+
if 'plug' in data and data['plug'] is not None:
|
859
|
+
if 'connection' in data['plug'] and data['plug']['connection'] is not None:
|
860
|
+
if data['plug']['connection'] in [item.value for item in ChargingConnector.ChargingConnectorConnectionState]:
|
861
|
+
plug_state: ChargingConnector.ChargingConnectorConnectionState = \
|
862
|
+
ChargingConnector.ChargingConnectorConnectionState(data['plug']['connection'])
|
863
|
+
else:
|
864
|
+
LOG_API.info('Unknown plug state %s', data['plug']['connection'])
|
865
|
+
plug_state = ChargingConnector.ChargingConnectorConnectionState.UNKNOWN
|
866
|
+
vehicle.charging.connector.connection_state._set_value(value=plug_state) # pylint: disable=protected-access
|
867
|
+
else:
|
868
|
+
vehicle.charging.connector.connection_state._set_value(value=None) # pylint: disable=protected-access
|
869
|
+
if 'externalPower' in data['plug'] and data['plug']['externalPower'] is not None:
|
870
|
+
if data['plug']['externalPower'] in [item.value for item in ChargingConnector.ExternalPower]:
|
871
|
+
plug_power_state: ChargingConnector.ExternalPower = \
|
872
|
+
ChargingConnector.ExternalPower(data['plug']['externalPower'])
|
873
|
+
else:
|
874
|
+
LOG_API.info('Unknown plug power state %s', data['plug']['externalPower'])
|
875
|
+
plug_power_state = ChargingConnector.ExternalPower.UNKNOWN
|
876
|
+
vehicle.charging.connector.external_power._set_value(value=plug_power_state) # pylint: disable=protected-access
|
877
|
+
else:
|
878
|
+
vehicle.charging.connector.external_power._set_value(None) # pylint: disable=protected-access
|
879
|
+
if 'lock' in data['plug'] and data['plug']['lock'] is not None:
|
880
|
+
if data['plug']['lock'] in [item.value for item in ChargingConnector.ChargingConnectorLockState]:
|
881
|
+
plug_lock_state: ChargingConnector.ChargingConnectorLockState = \
|
882
|
+
ChargingConnector.ChargingConnectorLockState(data['plug']['lock'])
|
883
|
+
else:
|
884
|
+
LOG_API.info('Unknown plug lock state %s', data['plug']['lock'])
|
885
|
+
plug_lock_state = ChargingConnector.ChargingConnectorLockState.UNKNOWN
|
886
|
+
vehicle.charging.connector.lock_state._set_value(value=plug_lock_state) # pylint: disable=protected-access
|
887
|
+
else:
|
888
|
+
vehicle.charging.connector.lock_state._set_value(None) # pylint: disable=protected-access
|
889
|
+
log_extra_keys(LOG_API, 'plug', data['plug'], {'connection', 'externalPower', 'lock'})
|
890
|
+
log_extra_keys(LOG_API, f'https://ola.prod.code.seat.cloud.vwgroup.com/v1/vehicles/{vin}/charging/status', data,
|
891
|
+
{'state', 'battery', 'charging', 'plug'})
|
892
|
+
|
893
|
+
url = f'https://ola.prod.code.seat.cloud.vwgroup.com/v1/vehicles/{vin}/charging/settings'
|
894
|
+
data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
895
|
+
if data is not None:
|
896
|
+
if 'maxChargeCurrentAc' in data and data['maxChargeCurrentAc'] is not None:
|
897
|
+
if data['maxChargeCurrentAc']:
|
898
|
+
vehicle.charging.settings.maximum_current._set_value(value=16, # pylint: disable=protected-access
|
899
|
+
unit=Current.A)
|
900
|
+
else:
|
901
|
+
vehicle.charging.settings.maximum_current._set_value(value=6, # pylint: disable=protected-access
|
902
|
+
unit=Current.A)
|
903
|
+
else:
|
904
|
+
vehicle.charging.settings.maximum_current._set_value(None) # pylint: disable=protected-access
|
905
|
+
if 'defaultMaxTargetSocPercentage' in data and data['defaultMaxTargetSocPercentage'] is not None:
|
906
|
+
vehicle.charging.settings.target_level._set_value(data['defaultMaxTargetSocPercentage']) # pylint: disable=protected-access
|
907
|
+
else:
|
908
|
+
vehicle.charging.settings.target_level._set_value(None) # pylint: disable=protected-access
|
909
|
+
return vehicle
|
910
|
+
|
911
|
+
def fetch_image(self, vehicle: SeatCupraVehicle, no_cache: bool = False) -> SeatCupraVehicle:
|
912
|
+
"""
|
913
|
+
Fetches the image of a given SeatCupraVehicle.
|
914
|
+
|
915
|
+
This method retrieves the image of the vehicle from a remote server. It supports caching to avoid redundant downloads.
|
916
|
+
If caching is enabled and the image is found in the cache and is not expired, it will be loaded from the cache.
|
917
|
+
Otherwise, it will be downloaded from the server.
|
918
|
+
|
919
|
+
Args:
|
920
|
+
vehicle (SeatCupraVehicle): The vehicle object for which the image is to be fetched.
|
921
|
+
no_cache (bool, optional): If True, bypasses the cache and fetches the image directly from the server. Defaults to False.
|
922
|
+
|
923
|
+
Returns:
|
924
|
+
SeatCupraVehicle: The vehicle object with the fetched image added to its attributes.
|
925
|
+
|
926
|
+
Raises:
|
927
|
+
RetrievalError: If there is a connection error, chunked encoding error, read timeout, or retry error during the image retrieval process.
|
928
|
+
"""
|
929
|
+
if SUPPORT_IMAGES:
|
930
|
+
url: str = f'https://ola.prod.code.seat.cloud.vwgroup.com/v1/vehicles/{vehicle.vin.value}/renders'
|
931
|
+
data = self._fetch_data(url, session=self.session, allow_http_error=True, no_cache=no_cache)
|
932
|
+
if data is not None: # pylint: disable=too-many-nested-blocks
|
933
|
+
for image_id, image_url in data.items():
|
934
|
+
if image_id == 'isDefault':
|
935
|
+
continue
|
936
|
+
img = None
|
937
|
+
cache_date = None
|
938
|
+
if self.active_config['max_age'] is not None and self.session.cache is not None and image_url in self.session.cache:
|
939
|
+
img, cache_date_string = self.session.cache[image_url]
|
940
|
+
img = base64.b64decode(img) # pyright: ignore[reportPossiblyUnboundVariable]
|
941
|
+
img = Image.open(io.BytesIO(img)) # pyright: ignore[reportPossiblyUnboundVariable]
|
942
|
+
cache_date = datetime.fromisoformat(cache_date_string)
|
943
|
+
if img is None or self.active_config['max_age'] is None \
|
944
|
+
or (cache_date is not None and cache_date < (datetime.utcnow() - timedelta(seconds=self.active_config['max_age']))):
|
945
|
+
try:
|
946
|
+
image_download_response = requests.get(image_url, stream=True, timeout=180)
|
947
|
+
if image_download_response.status_code == requests.codes['ok']:
|
948
|
+
img = Image.open(image_download_response.raw) # pyright: ignore[reportPossiblyUnboundVariable]
|
949
|
+
if self.session.cache is not None:
|
950
|
+
buffered = io.BytesIO() # pyright: ignore[reportPossiblyUnboundVariable]
|
951
|
+
img.save(buffered, format="PNG")
|
952
|
+
img_str = base64.b64encode(buffered.getvalue()).decode("utf-8") # pyright: ignore[reportPossiblyUnboundVariable]
|
953
|
+
self.session.cache[image_url] = (img_str, str(datetime.utcnow()))
|
954
|
+
elif image_download_response.status_code == requests.codes['unauthorized']:
|
955
|
+
LOG.info('Server asks for new authorization')
|
956
|
+
self.session.login()
|
957
|
+
image_download_response = self.session.get(image_url, stream=True)
|
958
|
+
if image_download_response.status_code == requests.codes['ok']:
|
959
|
+
img = Image.open(image_download_response.raw) # pyright: ignore[reportPossiblyUnboundVariable]
|
960
|
+
if self.session.cache is not None:
|
961
|
+
buffered = io.BytesIO() # pyright: ignore[reportPossiblyUnboundVariable]
|
962
|
+
img.save(buffered, format="PNG")
|
963
|
+
img_str = base64.b64encode(buffered.getvalue()).decode("utf-8") # pyright: ignore[reportPossiblyUnboundVariable]
|
964
|
+
self.session.cache[image_url] = (img_str, str(datetime.utcnow()))
|
965
|
+
except requests.exceptions.ConnectionError as connection_error:
|
966
|
+
raise RetrievalError(f'Connection error: {connection_error}') from connection_error
|
967
|
+
except requests.exceptions.ChunkedEncodingError as chunked_encoding_error:
|
968
|
+
raise RetrievalError(f'Error: {chunked_encoding_error}') from chunked_encoding_error
|
969
|
+
except requests.exceptions.ReadTimeout as timeout_error:
|
970
|
+
raise RetrievalError(f'Timeout during read: {timeout_error}') from timeout_error
|
971
|
+
except requests.exceptions.RetryError as retry_error:
|
972
|
+
raise RetrievalError(f'Retrying failed: {retry_error}') from retry_error
|
973
|
+
if img is not None:
|
974
|
+
vehicle._car_images[image_id] = img # pylint: disable=protected-access
|
975
|
+
if image_id == 'side':
|
976
|
+
if 'car_picture' in vehicle.images.images:
|
977
|
+
vehicle.images.images['car_picture']._set_value(img) # pylint: disable=protected-access
|
978
|
+
else:
|
979
|
+
vehicle.images.images['car_picture'] = ImageAttribute(name="car_picture", parent=vehicle.images,
|
980
|
+
value=img, tags={'carconnectivity'})
|
795
981
|
return vehicle
|
796
982
|
|
797
983
|
def _record_elapsed(self, elapsed: timedelta) -> None:
|
@@ -907,6 +1093,168 @@ class Connector(BaseConnector):
|
|
907
1093
|
raise CommandError(f'Could not start/stop air conditioning ({command_response.status_code}: {command_response.text})')
|
908
1094
|
return command_arguments
|
909
1095
|
|
1096
|
+
def __on_spin(self, spin_command: SpinCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
1097
|
+
-> Union[str, Dict[str, Any]]:
|
1098
|
+
del spin_command
|
1099
|
+
if not isinstance(command_arguments, dict):
|
1100
|
+
raise CommandError('Command arguments are not a dictionary')
|
1101
|
+
if 'command' not in command_arguments:
|
1102
|
+
raise CommandError('Command argument missing')
|
1103
|
+
command_dict = {}
|
1104
|
+
if self.active_config['spin'] is None:
|
1105
|
+
raise CommandError('S-PIN is missing, please add S-PIN to your configuration or .netrc file')
|
1106
|
+
if 'spin' in command_arguments:
|
1107
|
+
command_dict['currentSpin'] = command_arguments['spin']
|
1108
|
+
else:
|
1109
|
+
if self.active_config['spin'] is None or self.active_config['spin'] == '':
|
1110
|
+
raise CommandError('S-PIN is missing, please add S-PIN to your configuration or .netrc file')
|
1111
|
+
command_dict['spin'] = self.active_config['spin']
|
1112
|
+
if command_arguments['command'] == SpinCommand.Command.VERIFY:
|
1113
|
+
url = f'https://ola.prod.code.seat.cloud.vwgroup.com/v2/users/{self.session.user_id}/spin/verify'
|
1114
|
+
else:
|
1115
|
+
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
1116
|
+
command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
1117
|
+
if command_response.status_code != requests.codes['ok']:
|
1118
|
+
LOG.error('Could not execute spin command (%s: %s)', command_response.status_code, command_response.text)
|
1119
|
+
raise CommandError(f'Could not execute spin command ({command_response.status_code}: {command_response.text})')
|
1120
|
+
else:
|
1121
|
+
LOG.info('Spin verify command executed successfully')
|
1122
|
+
return command_arguments
|
1123
|
+
|
1124
|
+
def __on_wake_sleep(self, wake_sleep_command: WakeSleepCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
1125
|
+
-> Union[str, Dict[str, Any]]:
|
1126
|
+
if wake_sleep_command.parent is None or wake_sleep_command.parent.parent is None \
|
1127
|
+
or not isinstance(wake_sleep_command.parent.parent, GenericVehicle):
|
1128
|
+
raise CommandError('Object hierarchy is not as expected')
|
1129
|
+
if not isinstance(command_arguments, dict):
|
1130
|
+
raise CommandError('Command arguments are not a dictionary')
|
1131
|
+
vehicle: GenericVehicle = wake_sleep_command.parent.parent
|
1132
|
+
vin: Optional[str] = vehicle.vin.value
|
1133
|
+
if vin is None:
|
1134
|
+
raise CommandError('VIN in object hierarchy missing')
|
1135
|
+
if 'command' not in command_arguments:
|
1136
|
+
raise CommandError('Command argument missing')
|
1137
|
+
if command_arguments['command'] == WakeSleepCommand.Command.WAKE:
|
1138
|
+
url = f'https://ola.prod.code.seat.cloud.vwgroup.com/v1/vehicles/{vin}/vehicle-wakeup/request'
|
1139
|
+
|
1140
|
+
command_response: requests.Response = self.session.post(url, data='{}', allow_redirects=True)
|
1141
|
+
if command_response.status_code not in (requests.codes['ok'], requests.codes['no_content']):
|
1142
|
+
LOG.error('Could not execute wake command (%s: %s)', command_response.status_code, command_response.text)
|
1143
|
+
raise CommandError(f'Could not execute wake command ({command_response.status_code}: {command_response.text})')
|
1144
|
+
elif command_arguments['command'] == WakeSleepCommand.Command.SLEEP:
|
1145
|
+
raise CommandError('Sleep command not supported by vehicle. Vehicle will put itself to sleep')
|
1146
|
+
else:
|
1147
|
+
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
1148
|
+
return command_arguments
|
1149
|
+
|
1150
|
+
def __on_honk_flash(self, honk_flash_command: HonkAndFlashCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
1151
|
+
-> Union[str, Dict[str, Any]]:
|
1152
|
+
if honk_flash_command.parent is None or honk_flash_command.parent.parent is None \
|
1153
|
+
or not isinstance(honk_flash_command.parent.parent, GenericVehicle):
|
1154
|
+
raise CommandError('Object hierarchy is not as expected')
|
1155
|
+
if not isinstance(command_arguments, dict):
|
1156
|
+
raise CommandError('Command arguments are not a dictionary')
|
1157
|
+
vehicle: GenericVehicle = honk_flash_command.parent.parent
|
1158
|
+
vin: Optional[str] = vehicle.vin.value
|
1159
|
+
if vin is None:
|
1160
|
+
raise CommandError('VIN in object hierarchy missing')
|
1161
|
+
if 'command' not in command_arguments:
|
1162
|
+
raise CommandError('Command argument missing')
|
1163
|
+
command_dict = {}
|
1164
|
+
if command_arguments['command'] in [HonkAndFlashCommand.Command.FLASH, HonkAndFlashCommand.Command.HONK_AND_FLASH]:
|
1165
|
+
if 'duration' in command_arguments:
|
1166
|
+
command_dict['durationInSeconds'] = command_arguments['duration']
|
1167
|
+
else:
|
1168
|
+
command_dict['durationInSeconds'] = 10
|
1169
|
+
command_dict['mode'] = command_arguments['command'].value
|
1170
|
+
command_dict['userPosition'] = {}
|
1171
|
+
if vehicle.position is None or vehicle.position.latitude is None or vehicle.position.longitude is None \
|
1172
|
+
or vehicle.position.latitude.value is None or vehicle.position.longitude.value is None \
|
1173
|
+
or not vehicle.position.latitude.enabled or not vehicle.position.longitude.enabled:
|
1174
|
+
raise CommandError('Can only execute honk and flash commands if vehicle position is known')
|
1175
|
+
command_dict['userPosition']['latitude'] = vehicle.position.latitude.value
|
1176
|
+
command_dict['userPosition']['longitude'] = vehicle.position.longitude.value
|
1177
|
+
|
1178
|
+
url = f'https://ola.prod.code.seat.cloud.vwgroup.com/v1/vehicles/{vin}/honk-and-flash'
|
1179
|
+
command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
1180
|
+
if command_response.status_code not in (requests.codes['ok'], requests.codes['no_content']):
|
1181
|
+
LOG.error('Could not execute honk or flash command (%s: %s)', command_response.status_code, command_response.text)
|
1182
|
+
raise CommandError(f'Could not execute honk or flash command ({command_response.status_code}: {command_response.text})')
|
1183
|
+
else:
|
1184
|
+
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
1185
|
+
return command_arguments
|
1186
|
+
|
1187
|
+
def __on_lock_unlock(self, lock_unlock_command: LockUnlockCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
1188
|
+
-> Union[str, Dict[str, Any]]:
|
1189
|
+
if lock_unlock_command.parent is None or lock_unlock_command.parent.parent is None \
|
1190
|
+
or lock_unlock_command.parent.parent.parent is None or not isinstance(lock_unlock_command.parent.parent.parent, GenericVehicle):
|
1191
|
+
raise CommandError('Object hierarchy is not as expected')
|
1192
|
+
if not isinstance(command_arguments, dict):
|
1193
|
+
raise SetterError('Command arguments are not a dictionary')
|
1194
|
+
vehicle: GenericVehicle = lock_unlock_command.parent.parent.parent
|
1195
|
+
vin: Optional[str] = vehicle.vin.value
|
1196
|
+
if vin is None:
|
1197
|
+
raise CommandError('VIN in object hierarchy missing')
|
1198
|
+
if 'command' not in command_arguments:
|
1199
|
+
raise CommandError('Command argument missing')
|
1200
|
+
command_dict = {}
|
1201
|
+
if 'spin' in command_arguments:
|
1202
|
+
command_dict['spin'] = command_arguments['spin']
|
1203
|
+
else:
|
1204
|
+
if self.active_config['spin'] is None:
|
1205
|
+
raise CommandError('S-PIN is missing, please add S-PIN to your configuration or .netrc file')
|
1206
|
+
command_dict['spin'] = self.active_config['spin']
|
1207
|
+
if command_arguments['command'] == LockUnlockCommand.Command.LOCK:
|
1208
|
+
url = f'https://ola.prod.code.seat.cloud.vwgroup.com/v1/vehicles/{vin}/access/lock'
|
1209
|
+
elif command_arguments['command'] == LockUnlockCommand.Command.UNLOCK:
|
1210
|
+
url = f'https://ola.prod.code.seat.cloud.vwgroup.com/v1/vehicles/{vin}/access/unlock'
|
1211
|
+
else:
|
1212
|
+
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
1213
|
+
command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
1214
|
+
if command_response.status_code != requests.codes['ok']:
|
1215
|
+
LOG.error('Could not execute locking command (%s: %s)', command_response.status_code, command_response.text)
|
1216
|
+
raise CommandError(f'Could not execute locking command ({command_response.status_code}: {command_response.text})')
|
1217
|
+
return command_arguments
|
1218
|
+
|
1219
|
+
def __on_air_conditioning_settings_change(self, attribute: GenericAttribute, value: Any) -> Any:
|
1220
|
+
"""
|
1221
|
+
Callback for the climatization setting change.
|
1222
|
+
"""
|
1223
|
+
if attribute.parent is None or not isinstance(attribute.parent, SeatCupraClimatization.Settings) \
|
1224
|
+
or attribute.parent.parent is None \
|
1225
|
+
or attribute.parent.parent.parent is None or not isinstance(attribute.parent.parent.parent, SeatCupraVehicle):
|
1226
|
+
raise SetterError('Object hierarchy is not as expected')
|
1227
|
+
settings: SeatCupraClimatization.Settings = attribute.parent
|
1228
|
+
vehicle: SeatCupraVehicle = attribute.parent.parent.parent
|
1229
|
+
vin: Optional[str] = vehicle.vin.value
|
1230
|
+
if vin is None:
|
1231
|
+
raise SetterError('VIN in object hierarchy missing')
|
1232
|
+
setting_dict = {}
|
1233
|
+
if settings.target_temperature.enabled and settings.target_temperature.value is not None:
|
1234
|
+
# Round target temperature to nearest 0.5
|
1235
|
+
# Check if the attribute changed is the target_temperature attribute
|
1236
|
+
if isinstance(attribute, TemperatureAttribute) and attribute.id == 'target_temperature':
|
1237
|
+
setting_dict['targetTemperature'] = round(value * 2) / 2
|
1238
|
+
else:
|
1239
|
+
setting_dict['targetTemperature'] = round(settings.target_temperature.value * 2) / 2
|
1240
|
+
if settings.target_temperature.unit == Temperature.C:
|
1241
|
+
setting_dict['targetTemperatureUnit'] = 'celsius'
|
1242
|
+
elif settings.target_temperature.unit == Temperature.F:
|
1243
|
+
setting_dict['targetTemperatureUnit'] = 'farenheit'
|
1244
|
+
else:
|
1245
|
+
setting_dict['targetTemperatureUnit'] = 'celsius'
|
1246
|
+
if isinstance(attribute, BooleanAttribute) and attribute.id == 'climatisation_without_external_power':
|
1247
|
+
setting_dict['climatisationWithoutExternalPower'] = value
|
1248
|
+
elif settings.climatization_without_external_power.enabled and settings.climatization_without_external_power.value is not None:
|
1249
|
+
setting_dict['climatisationWithoutExternalPower'] = settings.climatization_without_external_power.value
|
1250
|
+
|
1251
|
+
url: str = f'https://ola.prod.code.seat.cloud.vwgroup.com/v2/vehicles/{vin}/climatisation/settings'
|
1252
|
+
settings_response: requests.Response = self.session.post(url, data=json.dumps(setting_dict), allow_redirects=True)
|
1253
|
+
if settings_response.status_code not in [requests.codes['ok'], requests.codes['created']]:
|
1254
|
+
LOG.error('Could not set climatization settings (%s) %s', settings_response.status_code, settings_response.text)
|
1255
|
+
raise SetterError(f'Could not set value ({settings_response.status_code}): {settings_response.text}')
|
1256
|
+
return value
|
1257
|
+
|
910
1258
|
def get_version(self) -> str:
|
911
1259
|
return __version__
|
912
1260
|
|
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING
|
|
5
5
|
from carconnectivity.vehicle import GenericVehicle, ElectricVehicle, CombustionVehicle, HybridVehicle
|
6
6
|
|
7
7
|
from carconnectivity_connectors.seatcupra.capability import Capabilities
|
8
|
+
from carconnectivity_connectors.seatcupra.climatization import SeatCupraClimatization
|
8
9
|
|
9
10
|
SUPPORT_IMAGES = False
|
10
11
|
try:
|
@@ -38,14 +39,15 @@ class SeatCupraVehicle(GenericVehicle): # pylint: disable=too-many-instance-att
|
|
38
39
|
self.capabilities.parent = self
|
39
40
|
if SUPPORT_IMAGES:
|
40
41
|
self._car_images = origin._car_images
|
41
|
-
|
42
42
|
else:
|
43
43
|
super().__init__(vin=vin, garage=garage, managing_connector=managing_connector)
|
44
|
+
self.climatization = SeatCupraClimatization(origin=self.climatization)
|
44
45
|
self.capabilities: Capabilities = Capabilities(vehicle=self)
|
45
46
|
if SUPPORT_IMAGES:
|
46
47
|
self._car_images: Dict[str, Image.Image] = {}
|
47
48
|
|
48
49
|
|
50
|
+
|
49
51
|
class SeatCupraElectricVehicle(ElectricVehicle, SeatCupraVehicle):
|
50
52
|
"""
|
51
53
|
Represents a Seat/Cupra electric vehicle.
|
{carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/.flake8
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/.gitignore
RENAMED
File without changes
|
{carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/CHANGELOG.md
RENAMED
File without changes
|
{carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/LICENSE
RENAMED
File without changes
|
{carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/Makefile
RENAMED
File without changes
|
{carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/README.md
RENAMED
File without changes
|
File without changes
|
{carconnectivity_connector_seatcupra-0.1a2 → carconnectivity_connector_seatcupra-0.1a4}/setup.cfg
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|