carconnectivity-connector-skoda 0.1a16__tar.gz → 0.1a18__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.

Potentially problematic release.


This version of carconnectivity-connector-skoda might be problematic. Click here for more details.

Files changed (38) hide show
  1. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/PKG-INFO +1 -1
  2. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connector_skoda.egg-info/PKG-INFO +1 -1
  3. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connector_skoda.egg-info/SOURCES.txt +2 -0
  4. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connectors/skoda/_version.py +1 -1
  5. carconnectivity_connector_skoda-0.1a18/src/carconnectivity_connectors/skoda/charging.py +137 -0
  6. carconnectivity_connector_skoda-0.1a18/src/carconnectivity_connectors/skoda/climatization.py +41 -0
  7. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connectors/skoda/connector.py +307 -13
  8. carconnectivity_connector_skoda-0.1a18/src/carconnectivity_connectors/skoda/error.py +52 -0
  9. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connectors/skoda/mqtt_client.py +54 -5
  10. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connectors/skoda/vehicle.py +6 -0
  11. carconnectivity_connector_skoda-0.1a16/src/carconnectivity_connectors/skoda/charging.py +0 -67
  12. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/.flake8 +0 -0
  13. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  14. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  15. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/.github/dependabot.yml +0 -0
  16. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/.github/workflows/build.yml +0 -0
  17. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/.github/workflows/build_and_publish.yml +0 -0
  18. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/.github/workflows/codeql-analysis.yml +0 -0
  19. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/.gitignore +0 -0
  20. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/LICENSE +0 -0
  21. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/Makefile +0 -0
  22. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/README.md +0 -0
  23. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/doc/Config.md +0 -0
  24. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/pyproject.toml +0 -0
  25. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/setup.cfg +0 -0
  26. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/setup_requirements.txt +0 -0
  27. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connector_skoda.egg-info/dependency_links.txt +0 -0
  28. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connector_skoda.egg-info/requires.txt +0 -0
  29. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connector_skoda.egg-info/top_level.txt +0 -0
  30. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connectors/skoda/__init__.py +0 -0
  31. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connectors/skoda/auth/__init__.py +0 -0
  32. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connectors/skoda/auth/auth_util.py +0 -0
  33. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connectors/skoda/auth/helpers/blacklist_retry.py +0 -0
  34. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connectors/skoda/auth/my_skoda_session.py +0 -0
  35. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connectors/skoda/auth/openid_session.py +0 -0
  36. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connectors/skoda/auth/session_manager.py +0 -0
  37. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connectors/skoda/auth/skoda_web_session.py +0 -0
  38. {carconnectivity_connector_skoda-0.1a16 → carconnectivity_connector_skoda-0.1a18}/src/carconnectivity_connectors/skoda/capability.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: carconnectivity-connector-skoda
3
- Version: 0.1a16
3
+ Version: 0.1a18
4
4
  Summary: CarConnectivity connector for Skoda services
5
5
  Author: Till Steinbach
6
6
  License: MIT License
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: carconnectivity-connector-skoda
3
- Version: 0.1a16
3
+ Version: 0.1a18
4
4
  Summary: CarConnectivity connector for Skoda services
5
5
  Author: Till Steinbach
6
6
  License: MIT License
@@ -21,7 +21,9 @@ src/carconnectivity_connectors/skoda/__init__.py
21
21
  src/carconnectivity_connectors/skoda/_version.py
22
22
  src/carconnectivity_connectors/skoda/capability.py
23
23
  src/carconnectivity_connectors/skoda/charging.py
24
+ src/carconnectivity_connectors/skoda/climatization.py
24
25
  src/carconnectivity_connectors/skoda/connector.py
26
+ src/carconnectivity_connectors/skoda/error.py
25
27
  src/carconnectivity_connectors/skoda/mqtt_client.py
26
28
  src/carconnectivity_connectors/skoda/vehicle.py
27
29
  src/carconnectivity_connectors/skoda/auth/__init__.py
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.1a16'
15
+ __version__ = version = '0.1a18'
16
16
  __version_tuple__ = version_tuple = (0, 1)
@@ -0,0 +1,137 @@
1
+ """
2
+ Module for charging for skoda vehicles.
3
+ """
4
+ from __future__ import annotations
5
+ from typing import TYPE_CHECKING
6
+
7
+ from enum import Enum
8
+
9
+ from carconnectivity.charging import Charging
10
+ from carconnectivity.objects import GenericObject
11
+ from carconnectivity.vehicle import ElectricVehicle
12
+ from carconnectivity.attributes import BooleanAttribute, EnumAttribute, StringAttribute
13
+
14
+ from carconnectivity_connectors.skoda.error import Error
15
+
16
+ if TYPE_CHECKING:
17
+ from typing import Optional, Dict
18
+
19
+
20
+ class SkodaCharging(Charging): # pylint: disable=too-many-instance-attributes
21
+ """
22
+ SkodaCharging class for handling Skoda vehicle charging information.
23
+
24
+ This class extends the Charging class and includes an enumeration of various
25
+ charging states specific to Skoda vehicles.
26
+ """
27
+ def __init__(self, vehicle: ElectricVehicle | None = None, origin: Optional[Charging] = None) -> None:
28
+ if origin is not None:
29
+ super().__init__(origin=origin)
30
+ self.settings: Charging.Settings = SkodaCharging.Settings(origin=origin.settings)
31
+ else:
32
+ super().__init__(vehicle=vehicle)
33
+ self.settings: Charging.Settings = SkodaCharging.Settings(origin=self.settings)
34
+ self.errors: Dict[str, Error] = {}
35
+ self.is_in_saved_location: BooleanAttribute = BooleanAttribute("is_in_saved_location", parent=self)
36
+
37
+ class Settings(Charging.Settings):
38
+ """
39
+ This class represents the settings for a skoda car charging.
40
+ """
41
+ def __init__(self, parent: Optional[GenericObject] = None, origin: Optional[Charging.Settings] = None) -> None:
42
+ if origin is not None:
43
+ super().__init__(origin=origin)
44
+ else:
45
+ super().__init__(parent=parent)
46
+ self.preferred_charge_mode: EnumAttribute = EnumAttribute("preferred_charge_mode", parent=self)
47
+ self.available_charge_modes: StringAttribute = StringAttribute("available_charge_modes", parent=self)
48
+ self.charging_care_mode: EnumAttribute = EnumAttribute("charging_care_mode", parent=self)
49
+ self.battery_support: EnumAttribute = EnumAttribute("battery_support", parent=self)
50
+
51
+ class SkodaChargingState(Enum,):
52
+ """
53
+ Enum representing the various charging states for a Skoda vehicle.
54
+
55
+ Attributes:
56
+ OFF: The vehicle is not charging.
57
+ READY_FOR_CHARGING: The vehicle is ready to start charging.
58
+ NOT_READY_FOR_CHARGING: The vehicle is not ready to start charging.
59
+ CONSERVATION: The vehicle is in conservation mode.
60
+ CHARGE_PURPOSE_REACHED_NOT_CONSERVATION_CHARGING: The vehicle has reached its charging purpose and is not in conservation charging mode.
61
+ CHARGE_PURPOSE_REACHED_CONSERVATION: The vehicle has reached its charging purpose and is in conservation charging mode.
62
+ CHARGING: The vehicle is currently charging.
63
+ ERROR: There is an error in the charging process.
64
+ UNSUPPORTED: The charging state is unsupported.
65
+ DISCHARGING: The vehicle is discharging.
66
+ UNKNOWN: The charging state is unknown.
67
+ """
68
+ OFF = 'off'
69
+ CONNECT_CABLE = 'connectCable'
70
+ READY_FOR_CHARGING = 'readyForCharging'
71
+ NOT_READY_FOR_CHARGING = 'notReadyForCharging'
72
+ CONSERVATION = 'conservation'
73
+ CHARGE_PURPOSE_REACHED_NOT_CONSERVATION_CHARGING = 'chargePurposeReachedAndNotConservationCharging'
74
+ CHARGE_PURPOSE_REACHED_CONSERVATION = 'chargePurposeReachedAndConservation'
75
+ CHARGING = 'charging'
76
+ ERROR = 'error'
77
+ UNSUPPORTED = 'unsupported'
78
+ DISCHARGING = 'discharging'
79
+ UNKNOWN = 'unknown charging state'
80
+
81
+ class SkodaChargeMode(Enum,):
82
+ """
83
+ Enum class representing different Skoda charge modes.
84
+
85
+ Attributes:
86
+ HOME_STORAGE_CHARGING (str): Charge mode for home storage charging.
87
+ IMMEDIATE_DISCHARGING (str): Charge mode for immediate discharging.
88
+ ONLY_OWN_CURRENT (str): Charge mode for using only own current.
89
+ PREFERRED_CHARGING_TIMES (str): Charge mode for preferred charging times.
90
+ TIMER_CHARGING_WITH_CLIMATISATION (str): Charge mode for timer charging with climatisation.
91
+ TIMER (str): Charge mode for timer-based charging.
92
+ MANUAL (str): Charge mode for manual charging.
93
+ OFF (str): Charge mode for turning off charging.
94
+ """
95
+ HOME_STORAGE_CHARGING = 'HOME_STORAGE_CHARGING'
96
+ IMMEDIATE_DISCHARGING = 'IMMEDIATE_DISCHARGING'
97
+ ONLY_OWN_CURRENT = 'ONLY_OWN_CURRENT'
98
+ PREFERRED_CHARGING_TIMES = 'PREFERRED_CHARGING_TIMES'
99
+ TIMER_CHARGING_WITH_CLIMATISATION = 'TIMER_CHARGING_WITH_CLIMATISATION'
100
+ TIMER = 'timer'
101
+ MANUAL = 'manual'
102
+ OFF = 'off'
103
+ UNKNOWN = 'unknown charge mode'
104
+
105
+ class SkodaChargingCareMode(Enum,):
106
+ """
107
+ Enum representing the charging care mode for Skoda vehicles.
108
+ """
109
+ ACTIVATED = 'ACTIVATED'
110
+ DEACTIVATED = 'DEACTIVATED'
111
+ UNKNOWN = 'UNKNOWN'
112
+
113
+ class SkodaBatterySupport(Enum,):
114
+ """
115
+ SkodaBatterySupport is an enumeration that represents the different states of battery support for Skoda vehicles.
116
+ """
117
+ ENABLED = 'ENABLED'
118
+ DISABLED = 'DISABLED'
119
+ NOT_ALLOWED = 'NOT_ALLOWED'
120
+ UNKNOWN = 'UNKNOWN'
121
+
122
+
123
+ # Mapping of Skoda charging states to generic charging states
124
+ mapping_skoda_charging_state: Dict[SkodaCharging.SkodaChargingState, Charging.ChargingState] = {
125
+ SkodaCharging.SkodaChargingState.OFF: Charging.ChargingState.OFF,
126
+ SkodaCharging.SkodaChargingState.CONNECT_CABLE: Charging.ChargingState.OFF,
127
+ SkodaCharging.SkodaChargingState.READY_FOR_CHARGING: Charging.ChargingState.READY_FOR_CHARGING,
128
+ SkodaCharging.SkodaChargingState.NOT_READY_FOR_CHARGING: Charging.ChargingState.OFF,
129
+ SkodaCharging.SkodaChargingState.CONSERVATION: Charging.ChargingState.CONSERVATION,
130
+ SkodaCharging.SkodaChargingState.CHARGE_PURPOSE_REACHED_NOT_CONSERVATION_CHARGING: Charging.ChargingState.READY_FOR_CHARGING,
131
+ SkodaCharging.SkodaChargingState.CHARGE_PURPOSE_REACHED_CONSERVATION: Charging.ChargingState.CONSERVATION,
132
+ SkodaCharging.SkodaChargingState.CHARGING: Charging.ChargingState.CHARGING,
133
+ SkodaCharging.SkodaChargingState.ERROR: Charging.ChargingState.ERROR,
134
+ SkodaCharging.SkodaChargingState.UNSUPPORTED: Charging.ChargingState.UNSUPPORTED,
135
+ SkodaCharging.SkodaChargingState.DISCHARGING: Charging.ChargingState.DISCHARGING,
136
+ SkodaCharging.SkodaChargingState.UNKNOWN: Charging.ChargingState.UNKNOWN
137
+ }
@@ -0,0 +1,41 @@
1
+ """
2
+ Module for charging for skoda 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 ElectricVehicle
10
+
11
+ from carconnectivity_connectors.skoda.error import Error
12
+
13
+ if TYPE_CHECKING:
14
+ from typing import Optional, Dict
15
+
16
+
17
+ class SkodaClimatization(Climatization): # pylint: disable=too-many-instance-attributes
18
+ """
19
+ SkodaClimatization class for handling Skoda vehicle climatization information.
20
+
21
+ This class extends the Climatization class and includes an enumeration of various
22
+ charging states specific to Skoda vehicles.
23
+ """
24
+ def __init__(self, vehicle: ElectricVehicle | None = None, origin: Optional[Climatization] = None) -> None:
25
+ if origin is not None:
26
+ super().__init__(origin=origin)
27
+ self.settings: Climatization.Settings = SkodaClimatization.Settings(origin=origin.settings)
28
+ else:
29
+ super().__init__(vehicle=vehicle)
30
+ self.settings: Climatization.Settings = SkodaClimatization.Settings(origin=self.settings)
31
+ self.errors: Dict[str, Error] = {}
32
+
33
+ class Settings(Climatization.Settings):
34
+ """
35
+ This class represents the settings for a skoda car climatiation.
36
+ """
37
+ def __init__(self, parent: Optional[GenericObject] = None, origin: Optional[Climatization.Settings] = None) -> None:
38
+ if origin is not None:
39
+ super().__init__(origin=origin)
40
+ else:
41
+ super().__init__(parent=parent)
@@ -15,9 +15,9 @@ from carconnectivity.errors import AuthenticationError, TooManyRequestsError, Re
15
15
  TemporaryAuthenticationError, ConfigurationError
16
16
  from carconnectivity.util import robust_time_parse, log_extra_keys, config_remove_credentials
17
17
  from carconnectivity.units import Length, Speed, Power, Temperature
18
- # from carconnectivity.doors import Doors
19
- # from carconnectivity.windows import Windows
20
- # from carconnectivity.lights import Lights
18
+ from carconnectivity.doors import Doors
19
+ from carconnectivity.windows import Windows
20
+ from carconnectivity.lights import Lights
21
21
  from carconnectivity.drive import GenericDrive, ElectricDrive, CombustionDrive
22
22
  from carconnectivity.attributes import BooleanAttribute, DurationAttribute
23
23
  from carconnectivity.charging import Charging
@@ -31,11 +31,13 @@ from carconnectivity_connectors.skoda.auth.my_skoda_session import MySkodaSessio
31
31
  from carconnectivity_connectors.skoda.vehicle import SkodaVehicle, SkodaElectricVehicle, SkodaCombustionVehicle, SkodaHybridVehicle
32
32
  from carconnectivity_connectors.skoda.capability import Capability
33
33
  from carconnectivity_connectors.skoda.charging import SkodaCharging, mapping_skoda_charging_state
34
+ from carconnectivity_connectors.skoda.climatization import SkodaClimatization
35
+ from carconnectivity_connectors.skoda.error import Error
34
36
  from carconnectivity_connectors.skoda._version import __version__
35
37
  from carconnectivity_connectors.skoda.mqtt_client import SkodaMQTTClient
36
38
 
37
39
  if TYPE_CHECKING:
38
- from typing import Dict, List, Optional, Any
40
+ from typing import Dict, List, Optional, Any, Set
39
41
 
40
42
  from carconnectivity.carconnectivity import CarConnectivity
41
43
 
@@ -308,6 +310,7 @@ class Connector(BaseConnector):
308
310
  for vin in set(garage.list_vehicle_vins()):
309
311
  vehicle_to_update: Optional[GenericVehicle] = garage.get_vehicle(vin)
310
312
  if vehicle_to_update is not None and isinstance(vehicle_to_update, SkodaVehicle) and vehicle_to_update.is_managed_by_connector(self):
313
+ vehicle_to_update = self.fetch_vehicle_status(vehicle_to_update)
311
314
  vehicle_to_update = self.fetch_driving_range(vehicle_to_update)
312
315
  if vehicle_to_update.capabilities is not None and vehicle_to_update.capabilities.enabled:
313
316
  if vehicle_to_update.capabilities.has_capability('PARKING_POSITION'):
@@ -340,6 +343,12 @@ class Connector(BaseConnector):
340
343
  captured_at: datetime = robust_time_parse(data['carCapturedTimestamp'])
341
344
  else:
342
345
  raise APIError('Could not fetch charging, carCapturedTimestamp missing')
346
+ if 'isVehicleInSavedLocation' in data and data['isVehicleInSavedLocation'] is not None:
347
+ if vehicle.charging is not None:
348
+ if not isinstance(vehicle.charging, SkodaCharging):
349
+ vehicle.charging = SkodaCharging(origin=vehicle.charging)
350
+ # pylint: disable-next=protected-access
351
+ vehicle.charging.is_in_saved_location._set_value(data['isVehicleInSavedLocation'], measured=captured_at)
343
352
  if 'status' in data and data['status'] is not None:
344
353
  if 'state' in data['status'] and data['status']['state'] is not None:
345
354
  if data['status']['state'] in [item.name for item in SkodaCharging.SkodaChargingState]:
@@ -404,7 +413,114 @@ class Connector(BaseConnector):
404
413
  'state',
405
414
  'chargeType',
406
415
  'battery'})
407
- log_extra_keys(LOG_API, 'charging data', data, {'carCapturedTimestamp', 'status'})
416
+ if 'settings' in data and data['settings'] is not None:
417
+ if 'targetStateOfChargeInPercent' in data['settings'] and data['settings']['targetStateOfChargeInPercent'] is not None \
418
+ and vehicle.charging is not None and vehicle.charging.settings is not None:
419
+ # pylint: disable-next=protected-access
420
+ vehicle.charging.settings.target_level._set_value(value=data['settings']['targetStateOfChargeInPercent'], measured=captured_at)
421
+ else:
422
+ vehicle.charging.settings.target_level._set_value(None, measured=captured_at) # pylint: disable=protected-access
423
+ if 'maxChargeCurrentAc' in data['settings'] and data['settings']['maxChargeCurrentAc'] is not None \
424
+ and vehicle.charging is not None and vehicle.charging.settings is not None:
425
+ if data['settings']['maxChargeCurrentAc'] == 'MAXIMUM':
426
+ vehicle.charging.settings.maximum_current._set_value(value=11, measured=captured_at) # pylint: disable=protected-access
427
+ elif data['settings']['maxChargeCurrentAc'] == 'REDUCED':
428
+ vehicle.charging.settings.maximum_current._set_value(value=6, measured=captured_at) # pylint: disable=protected-access
429
+ else:
430
+ LOG_API.info('Unknown maxChargeCurrentAc %s not in %s', data['settings']['maxChargeCurrentAc'], ['MAXIMUM', 'REDUCED'])
431
+ vehicle.charging.settings.maximum_current._set_value(None, measured=captured_at) # pylint: disable=protected-access
432
+ else:
433
+ vehicle.charging.settings.maximum_current._set_value(None, measured=captured_at) # pylint: disable=protected-access
434
+ if 'autoUnlockPlugWhenCharged' in data['settings'] and data['settings']['autoUnlockPlugWhenCharged'] is not None:
435
+ if data['settings']['autoUnlockPlugWhenCharged'] in ['ON', 'PERMANENT']:
436
+ vehicle.charging.settings.auto_unlock._set_value(True, measured=captured_at) # pylint: disable=protected-access
437
+ elif data['settings']['autoUnlockPlugWhenCharged'] == 'OFF':
438
+ vehicle.charging.settings.auto_unlock._set_value(False, measured=captured_at) # pylint: disable=protected-access
439
+ else:
440
+ LOG_API.info('Unknown autoUnlockPlugWhenCharged %s not in %s', data['settings']['autoUnlockPlugWhenCharged'],
441
+ ['ON', 'PERMANENT', 'OFF'])
442
+ vehicle.charging.settings.auto_unlock._set_value(None, measured=captured_at) # pylint: disable=protected-access
443
+ if 'preferredChargeMode' in data['settings'] and data['settings']['preferredChargeMode'] is not None:
444
+ if not isinstance(vehicle.charging, SkodaCharging):
445
+ vehicle.charging = SkodaCharging(origin=vehicle.charging)
446
+ if data['settings']['preferredChargeMode'] in [item.name for item in SkodaCharging.SkodaChargeMode]:
447
+ preferred_charge_mode: SkodaCharging.SkodaChargeMode = SkodaCharging.SkodaChargeMode[data['settings']['preferredChargeMode']]
448
+ else:
449
+ LOG_API.info('Unkown charge mode %s not in %s', data['settings']['preferredChargeMode'], str(SkodaCharging.SkodaChargeMode))
450
+ preferred_charge_mode = SkodaCharging.SkodaChargeMode.UNKNOWN
451
+
452
+ if isinstance(vehicle.charging.settings, SkodaCharging.Settings):
453
+ # pylint: disable-next=protected-access
454
+ vehicle.charging.settings.preferred_charge_mode._set_value(value=preferred_charge_mode, measured=captured_at)
455
+ else:
456
+ if vehicle.charging is not None and isinstance(vehicle.charging.settings, SkodaCharging.Settings):
457
+ vehicle.charging.settings.preferred_charge_mode._set_value(None, measured=captured_at) # pylint: disable=protected-access
458
+ if 'availableChargeModes' in data['settings'] and data['settings']['availableChargeModes'] is not None:
459
+ if not isinstance(vehicle.charging, SkodaCharging):
460
+ vehicle.charging = SkodaCharging(origin=vehicle.charging)
461
+ available_charge_modes: list[str] = data['settings']['availableChargeModes']
462
+ if vehicle.charging is not None and isinstance(vehicle.charging.settings, SkodaCharging.Settings):
463
+ # pylint: disable-next=protected-access
464
+ vehicle.charging.settings.available_charge_modes._set_value('.'.join(available_charge_modes), measured=captured_at)
465
+ else:
466
+ if vehicle.charging is not None and isinstance(vehicle.charging.settings, SkodaCharging.Settings):
467
+ vehicle.charging.settings.available_charge_modes._set_value(None, measured=captured_at) # pylint: disable=protected-access
468
+ if 'chargingCareMode' in data['settings'] and data['settings']['chargingCareMode'] is not None:
469
+ if not isinstance(vehicle.charging, SkodaCharging):
470
+ vehicle.charging = SkodaCharging(origin=vehicle.charging)
471
+ if data['settings']['chargingCareMode'] in [item.name for item in SkodaCharging.SkodaChargingCareMode]:
472
+ charge_mode: SkodaCharging.SkodaChargingCareMode = SkodaCharging.SkodaChargingCareMode[data['settings']['chargingCareMode']]
473
+ else:
474
+ LOG_API.info('Unknown charging care mode %s not in %s', data['settings']['chargingCareMode'], str(SkodaCharging.SkodaChargingCareMode))
475
+ charge_mode = SkodaCharging.SkodaChargingCareMode.UNKNOWN
476
+ if vehicle.charging is not None and isinstance(vehicle.charging.settings, SkodaCharging.Settings):
477
+ # pylint: disable-next=protected-access
478
+ vehicle.charging.settings.charging_care_mode._set_value(value=charge_mode, measured=captured_at)
479
+ else:
480
+ if vehicle.charging is not None and isinstance(vehicle.charging.settings, SkodaCharging.Settings):
481
+ vehicle.charging.settings.charging_care_mode._set_value(None, measured=captured_at) # pylint: disable=protected-access
482
+ if 'batterySupport' in data['settings'] and data['settings']['batterySupport'] is not None:
483
+ if not isinstance(vehicle.charging, SkodaCharging):
484
+ vehicle.charging = SkodaCharging(origin=vehicle.charging)
485
+ if data['settings']['batterySupport'] in [item.name for item in SkodaCharging.SkodaBatterySupport]:
486
+ battery_support: SkodaCharging.SkodaBatterySupport = SkodaCharging.SkodaBatterySupport[data['settings']['batterySupport']]
487
+ else:
488
+ LOG_API.info('Unknown battery support %s not in %s', data['settings']['batterySupport'], str(SkodaCharging.SkodaBatterySupport))
489
+ battery_support = SkodaCharging.SkodaBatterySupport.UNKNOWN
490
+ if vehicle.charging is not None and isinstance(vehicle.charging.settings, SkodaCharging.Settings):
491
+ # pylint: disable-next=protected-access
492
+ vehicle.charging.settings.battery_support._set_value(value=battery_support, measured=captured_at)
493
+ else:
494
+ if vehicle.charging is not None and isinstance(vehicle.charging.settings, SkodaCharging.Settings):
495
+ vehicle.charging.settings.battery_support._set_value(None, measured=captured_at) # pylint: disable=protected-access
496
+ log_extra_keys(LOG_API, 'settings', data['settings'], {'targetStateOfChargeInPercent', 'maxChargeCurrentAc', 'autoUnlockPlugWhenCharged',
497
+ 'preferredChargeMode', 'availableChargeModes', 'chargingCareMode', 'batterySupport'})
498
+ if 'errors' in data and data['errors'] is not None:
499
+ found_errors: Set[str] = set()
500
+ if not isinstance(vehicle.charging, SkodaCharging):
501
+ vehicle.charging = SkodaCharging(origin=vehicle.charging)
502
+ for error_dict in data['errors']:
503
+ if 'type' in error_dict and error_dict['type'] is not None:
504
+ if error_dict['type'] not in vehicle.charging.errors:
505
+ error: Error = Error(object_id=error_dict['type'])
506
+ else:
507
+ error = vehicle.charging.errors[error_dict['type']]
508
+ if error_dict['type'] in [item.name for item in Error.ChargingError]:
509
+ error_type: Error.ChargingError = Error.ChargingError[error_dict['type']]
510
+ else:
511
+ LOG_API.info('Unknown charging error type %s not in %s', error_dict['type'], str(Error.ChargingError))
512
+ error_type = Error.ChargingError.UNKNOWN
513
+ error.type._set_value(error_type, measured=captured_at) # pylint: disable=protected-access
514
+ if 'description' in error_dict and error_dict['description'] is not None:
515
+ error.description._set_value(error_dict['description'], measured=captured_at) # pylint: disable=protected-access
516
+ log_extra_keys(LOG_API, 'errors', error_dict, {'type', 'description'})
517
+ if vehicle.charging is not None and vehicle.charging.errors is not None and len(vehicle.charging.errors) > 0:
518
+ for error_id in vehicle.charging.errors.keys()-found_errors:
519
+ vehicle.charging.errors.pop(error_id)
520
+ else:
521
+ if isinstance(vehicle.charging, SkodaCharging):
522
+ vehicle.charging.errors.clear()
523
+ log_extra_keys(LOG_API, 'charging data', data, {'carCapturedTimestamp', 'status', 'isVehicleInSavedLocation', 'errors', 'settings'})
408
524
  return vehicle
409
525
 
410
526
  def fetch_position(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
@@ -522,15 +638,16 @@ class Connector(BaseConnector):
522
638
  LOG_API.info('Unknown temperature unit for targetTemperature in air-conditioning %s', data['targetTemperature']['unitInCar'])
523
639
  if 'temperatureValue' in data['targetTemperature'] and data['targetTemperature']['temperatureValue'] is not None:
524
640
  # pylint: disable-next=protected-access
525
- vehicle.climatization.target_temperature._set_value(value=data['targetTemperature']['temperatureValue'],
526
- measured=captured_at,
527
- unit=unit)
641
+ vehicle.climatization.settings.target_temperature._set_value(value=data['targetTemperature']['temperatureValue'],
642
+ measured=captured_at,
643
+ unit=unit)
528
644
  else:
529
- vehicle.climatization.target_temperature._set_value(value=None, measured=captured_at, unit=unit) # pylint: disable=protected-access
645
+ # pylint: disable-next=protected-access
646
+ vehicle.climatization.settings.target_temperature._set_value(value=None, measured=captured_at, unit=unit)
530
647
  log_extra_keys(LOG_API, 'targetTemperature', data['targetTemperature'], {'unitInCar', 'temperatureValue'})
531
648
  else:
532
649
  # pylint: disable-next=protected-access
533
- vehicle.climatization.target_temperature._set_value(value=None, measured=captured_at, unit=Temperature.UNKNOWN)
650
+ vehicle.climatization.settings.target_temperature._set_value(value=None, measured=captured_at, unit=Temperature.UNKNOWN)
534
651
  if 'outsideTemperature' in data and data['outsideTemperature'] is not None:
535
652
  if 'carCapturedTimestamp' in data['outsideTemperature'] and data['outsideTemperature']['carCapturedTimestamp'] is not None:
536
653
  outside_captured_at: datetime = robust_time_parse(data['outsideTemperature']['carCapturedTimestamp'])
@@ -545,7 +662,7 @@ class Connector(BaseConnector):
545
662
  elif data['outsideTemperature']['temperatureUnit'] == 'KELVIN':
546
663
  unit = Temperature.K
547
664
  else:
548
- LOG_API.info('Unknown temperature unit for outsideTemperature in air-conditioning %s', data['targetTemperature']['temperatureUnit'])
665
+ LOG_API.info('Unknown temperature unit for outsideTemperature in air-conditioning %s', data['outsideTemperature']['temperatureUnit'])
549
666
  if 'temperatureValue' in data['outsideTemperature'] and data['outsideTemperature']['temperatureValue'] is not None:
550
667
  # pylint: disable-next=protected-access
551
668
  vehicle.outside_temperature._set_value(value=data['outsideTemperature']['temperatureValue'],
@@ -560,6 +677,66 @@ class Connector(BaseConnector):
560
677
  log_extra_keys(LOG_API, 'targetTemperature', data['outsideTemperature'], {'carCapturedTimestamp', 'temperatureUnit', 'temperatureValue'})
561
678
  else:
562
679
  vehicle.outside_temperature._set_value(value=None, measured=None, unit=Temperature.UNKNOWN) # pylint: disable=protected-access
680
+ if 'airConditioningAtUnlock' in data and data['airConditioningAtUnlock'] is not None:
681
+ if vehicle.climatization is not None and vehicle.climatization.settings is not None:
682
+ if data['airConditioningAtUnlock'] is True:
683
+ # pylint: disable-next=protected-access
684
+ vehicle.climatization.settings.climatization_at_unlock._set_value(True, measured=captured_at)
685
+ elif data['airConditioningAtUnlock'] is False:
686
+ # pylint: disable-next=protected-access
687
+ vehicle.climatization.settings.climatization_at_unlock._set_value(False, measured=captured_at)
688
+ else:
689
+ # pylint: disable-next=protected-access
690
+ vehicle.climatization.settings.climatization_at_unlock._set_value(None, measured=captured_at)
691
+ else:
692
+ if vehicle.climatization is not None and vehicle.climatization.settings is not None:
693
+ # pylint: disable-next=protected-access
694
+ vehicle.climatization.settings.climatization_at_unlock._set_value(None, measured=captured_at)
695
+ if 'steeringWheelPosition' in data and data['steeringWheelPosition'] is not None:
696
+ if vehicle.specification is not None:
697
+ if data['steeringWheelPosition'] in [item.name for item in GenericVehicle.VehicleSpecification.SteeringPosition]:
698
+ steering_wheel_position: GenericVehicle.VehicleSpecification.SteeringPosition = \
699
+ GenericVehicle.VehicleSpecification.SteeringPosition[data['steeringWheelPosition']]
700
+ else:
701
+ LOG_API.info('Unknown steering wheel position %s not in %s', data['steeringWheelPosition'],
702
+ str(GenericVehicle.VehicleSpecification.SteeringPosition))
703
+ steering_wheel_position = GenericVehicle.VehicleSpecification.SteeringPosition.UNKNOWN
704
+ # pylint: disable-next=protected-access
705
+ vehicle.specification.steering_wheel_position._set_value(value=steering_wheel_position, measured=captured_at)
706
+ else:
707
+ if vehicle.specification is not None:
708
+ # pylint: disable-next=protected-access
709
+ vehicle.specification.steering_wheel_position._set_value(None, measured=captured_at)
710
+ if 'windowHeatingEnabled' in data and data['windowHeatingEnabled'] is not None:
711
+ if vehicle.climatization is not None and vehicle.climatization.settings is not None:
712
+ if data['windowHeatingEnabled'] is True:
713
+ # pylint: disable-next=protected-access
714
+ vehicle.climatization.settings.window_heating._set_value(True, measured=captured_at)
715
+ elif data['windowHeatingEnabled'] is False:
716
+ # pylint: disable-next=protected-access
717
+ vehicle.climatization.settings.window_heating._set_value(False, measured=captured_at)
718
+ else:
719
+ # pylint: disable-next=protected-access
720
+ vehicle.climatization.settings.window_heating._set_value(None, measured=captured_at)
721
+ else:
722
+ if vehicle.climatization is not None and vehicle.climatization.settings is not None:
723
+ # pylint: disable-next=protected-access
724
+ vehicle.climatization.settings.window_heating._set_value(None, measured=captured_at)
725
+ if 'seatHeatingActivated' in data and data['seatHeatingActivated'] is not None:
726
+ if vehicle.climatization is not None and vehicle.climatization.settings is not None:
727
+ if data['seatHeatingActivated'] is True:
728
+ # pylint: disable-next=protected-access
729
+ vehicle.climatization.settings.seat_heating._set_value(True, measured=captured_at)
730
+ elif data['seatHeatingActivated'] is False:
731
+ # pylint: disable-next=protected-access
732
+ vehicle.climatization.settings.seat_heating._set_value(False, measured=captured_at)
733
+ else:
734
+ # pylint: disable-next=protected-access
735
+ vehicle.climatization.settings.seat_heating._set_value(None, measured=captured_at)
736
+ else:
737
+ if vehicle.climatization is not None and vehicle.climatization.settings is not None:
738
+ # pylint: disable-next=protected-access
739
+ vehicle.climatization.settings.seat_heating._set_value(None, measured=captured_at)
563
740
  if isinstance(vehicle, SkodaElectricVehicle):
564
741
  if 'chargerConnectionState' in data and data['chargerConnectionState'] is not None \
565
742
  and vehicle.charging is not None and vehicle.charging.connector is not None:
@@ -593,9 +770,35 @@ class Connector(BaseConnector):
593
770
  vehicle.charging.connector.lock_state._set_value(value=None, measured=captured_at)
594
771
  if 'windowHeatingState' in data and data['windowHeatingState'] is not None:
595
772
  pass
596
- log_extra_keys(LOG_API, 'air-condition', data, {'carCapturedTimestamp', 'state', 'estimatedDateTimeToReachTargetTemperature'
773
+ if 'errors' in data and data['errors'] is not None:
774
+ found_errors: Set[str] = set()
775
+ if not isinstance(vehicle.climatization, SkodaClimatization):
776
+ vehicle.climatization = SkodaClimatization(origin=vehicle.climatization)
777
+ for error_dict in data['errors']:
778
+ if 'type' in error_dict and error_dict['type'] is not None:
779
+ if error_dict['type'] not in vehicle.climatization.errors:
780
+ error: Error = Error(object_id=error_dict['type'])
781
+ else:
782
+ error = vehicle.climatization.errors[error_dict['type']]
783
+ if error_dict['type'] in [item.name for item in Error.ClimatizationError]:
784
+ error_type: Error.ClimatizationError = Error.ClimatizationError[error_dict['type']]
785
+ else:
786
+ LOG_API.info('Unknown climatization error type %s not in %s', error_dict['type'], str(Error.ClimatizationError))
787
+ error_type = Error.ClimatizationError.UNKNOWN
788
+ error.type._set_value(error_type, measured=captured_at) # pylint: disable=protected-access
789
+ if 'description' in error_dict and error_dict['description'] is not None:
790
+ error.description._set_value(error_dict['description'], measured=captured_at) # pylint: disable=protected-access
791
+ log_extra_keys(LOG_API, 'errors', error_dict, {'type', 'description'})
792
+ if vehicle.climatization is not None and vehicle.climatization.errors is not None and len(vehicle.climatization.errors) > 0:
793
+ for error_id in vehicle.climatization.errors.keys()-found_errors:
794
+ vehicle.climatization.errors.pop(error_id)
795
+ else:
796
+ if isinstance(vehicle.climatization, SkodaClimatization):
797
+ vehicle.climatization.errors.clear()
798
+ log_extra_keys(LOG_API, 'air-condition', data, {'carCapturedTimestamp', 'state', 'estimatedDateTimeToReachTargetTemperature',
597
799
  'targetTemperature', 'outsideTemperature', 'chargerConnectionState',
598
- 'chargerLockState'})
800
+ 'chargerLockState', 'airConditioningAtUnlock', 'steeringWheelPosition',
801
+ 'windowHeatingEnabled', 'seatHeatingActivated', 'errors'})
599
802
  return vehicle
600
803
 
601
804
  def fetch_vehicle_details(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
@@ -761,6 +964,97 @@ class Connector(BaseConnector):
761
964
  'secondaryEngineRange'})
762
965
  return vehicle
763
966
 
967
+ def fetch_vehicle_status(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
968
+ """
969
+ Fetches the status of a vehicle from other Skoda API.
970
+
971
+ Args:
972
+ vehicle (GenericVehicle): The vehicle object containing the VIN.
973
+
974
+ Returns:
975
+ None
976
+ """
977
+ vin = vehicle.vin.value
978
+ if vin is None:
979
+ raise APIError('VIN is missing')
980
+ url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/vehicle-status/{vin}'
981
+ vehicle_status_data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
982
+ if vehicle_status_data:
983
+ captured_at: datetime = robust_time_parse(vehicle_status_data['carCapturedTimestamp'])
984
+ if 'overall' in vehicle_status_data and vehicle_status_data['overall'] is not None:
985
+ if 'doorsLocked' in vehicle_status_data['overall'] and vehicle_status_data['overall']['doorsLocked'] is not None \
986
+ and vehicle.doors is not None:
987
+ if vehicle_status_data['overall']['doorsLocked'] == 'YES':
988
+ vehicle.doors.lock_state._set_value(Doors.LockState.LOCKED, measured=captured_at) # pylint: disable=protected-access
989
+ vehicle.doors.open_state._set_value(Doors.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
990
+ elif vehicle_status_data['overall']['doorsLocked'] == 'OPENED':
991
+ vehicle.doors.lock_state._set_value(Doors.LockState.UNLOCKED, measured=captured_at) # pylint: disable=protected-access
992
+ vehicle.doors.open_state._set_value(Doors.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
993
+ elif vehicle_status_data['overall']['doorsLocked'] == 'UNLOCKED':
994
+ vehicle.doors.lock_state._set_value(Doors.LockState.UNLOCKED, measured=captured_at) # pylint: disable=protected-access
995
+ vehicle.doors.open_state._set_value(Doors.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
996
+ elif vehicle_status_data['overall']['doorsLocked'] == 'TRUNK_OPENED':
997
+ vehicle.doors.lock_state._set_value(Doors.LockState.UNLOCKED, measured=captured_at) # pylint: disable=protected-access
998
+ vehicle.doors.open_state._set_value(Doors.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
999
+ elif vehicle_status_data['overall']['doorsLocked'] == 'UNKNOWN':
1000
+ vehicle.doors.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1001
+ vehicle.doors.open_state._set_value(Doors.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1002
+ else:
1003
+ LOG_API.info('Unknown doorsLocked state %s', vehicle_status_data['overall']['doorsLocked'])
1004
+ vehicle.doors.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1005
+ vehicle.doors.open_state._set_value(Doors.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1006
+ if 'locked' in vehicle_status_data['overall'] and vehicle_status_data['overall']['locked'] is not None:
1007
+ if vehicle_status_data['overall']['locked'] == 'YES':
1008
+ vehicle.doors.lock_state._set_value(Doors.LockState.LOCKED, measured=captured_at) # pylint: disable=protected-access
1009
+ elif vehicle_status_data['overall']['locked'] == 'NO':
1010
+ vehicle.doors.lock_state._set_value(Doors.LockState.UNLOCKED, measured=captured_at) # pylint: disable=protected-access
1011
+ elif vehicle_status_data['overall']['locked'] == 'UNKNOWN':
1012
+ vehicle.doors.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1013
+ else:
1014
+ LOG_API.info('Unknown locked state %s', vehicle_status_data['overall']['locked'])
1015
+ vehicle.doors.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1016
+ if 'doors' in vehicle_status_data['overall'] and vehicle_status_data['overall']['doors'] is not None:
1017
+ if vehicle_status_data['overall']['doors'] == 'CLOSED':
1018
+ vehicle.doors.open_state._set_value(Doors.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
1019
+ elif vehicle_status_data['overall']['doors'] == 'OPEN':
1020
+ vehicle.doors.open_state._set_value(Doors.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
1021
+ elif vehicle_status_data['overall']['doors'] == 'UNSUPPORTED':
1022
+ vehicle.doors.open_state._set_value(Doors.OpenState.UNSUPPORTED, measured=captured_at) # pylint: disable=protected-access
1023
+ elif vehicle_status_data['overall']['doors'] == 'UNKNOWN':
1024
+ vehicle.doors.open_state._set_value(Doors.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1025
+ else:
1026
+ LOG_API.info('Unknown doors state %s', vehicle_status_data['overall']['doors'])
1027
+ vehicle.doors.open_state._set_value(Doors.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1028
+ if 'windows' in vehicle_status_data['overall'] and vehicle_status_data['overall']['windows'] is not None:
1029
+ if vehicle_status_data['overall']['windows'] == 'CLOSED':
1030
+ vehicle.windows.open_state._set_value(Windows.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
1031
+ elif vehicle_status_data['overall']['windows'] == 'OPEN':
1032
+ vehicle.windows.open_state._set_value(Windows.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
1033
+ elif vehicle_status_data['overall']['windows'] == 'UNKNOWN':
1034
+ vehicle.windows.open_state._set_value(Windows.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1035
+ elif vehicle_status_data['overall']['windows'] == 'UNSUPPORTED':
1036
+ vehicle.windows.open_state._set_value(Windows.OpenState.UNSUPPORTED, measured=captured_at) # pylint: disable=protected-access
1037
+ else:
1038
+ LOG_API.info('Unknown windows state %s', vehicle_status_data['overall']['windows'])
1039
+ vehicle.windows.open_state._set_value(Windows.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1040
+ if 'lights' in vehicle_status_data['overall'] and vehicle_status_data['overall']['lights'] is not None:
1041
+ if vehicle_status_data['overall']['lights'] == 'ON':
1042
+ vehicle.lights.light_state._set_value(Lights.LightState.ON, measured=captured_at) # pylint: disable=protected-access
1043
+ elif vehicle_status_data['overall']['lights'] == 'OFF':
1044
+ vehicle.lights.light_state._set_value(Lights.LightState.OFF, measured=captured_at) # pylint: disable=protected-access
1045
+ elif vehicle_status_data['overall']['lights'] == 'UNKNOWN':
1046
+ vehicle.lights.light_state._set_value(Lights.LightState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1047
+ else:
1048
+ LOG_API.info('Unknown lights state %s', vehicle_status_data['overall']['lights'])
1049
+ vehicle.lights.light_state._set_value(Lights.LightState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1050
+ log_extra_keys(LOG_API, 'status overall', vehicle_status_data['overall'], {'doorsLocked',
1051
+ 'locked',
1052
+ 'doors',
1053
+ 'windows',
1054
+ 'lights'})
1055
+ log_extra_keys(LOG_API, f'/api/v2/vehicle-status/{vin}', vehicle_status_data, {'overall', 'carCapturedTimestamp'})
1056
+ return vehicle
1057
+
764
1058
  # def fetch_vehicle_status_second_api(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
765
1059
  # """
766
1060
  # Fetches the status of a vehicle from other Skoda API.
@@ -0,0 +1,52 @@
1
+ """Module for Skoda vehicle capability class."""
2
+ from __future__ import annotations
3
+ from typing import TYPE_CHECKING
4
+
5
+ from enum import Enum
6
+
7
+ from carconnectivity.objects import GenericObject
8
+ from carconnectivity.attributes import EnumAttribute, StringAttribute
9
+
10
+ if TYPE_CHECKING:
11
+ from typing import Optional
12
+
13
+
14
+ class Error(GenericObject):
15
+ """
16
+ Represents an error object in the car connectivity context.
17
+ """
18
+
19
+ def __init__(self, object_id, parent: Optional[GenericObject] = None) -> None:
20
+ super().__init__(object_id, parent=parent)
21
+ self.type: EnumAttribute = EnumAttribute("type", parent=self)
22
+ self.description: StringAttribute = StringAttribute("description", parent=self)
23
+
24
+ class ChargingError(Enum):
25
+ """
26
+ Enum representing various charging errors for Skoda car connectivity.
27
+
28
+ Attributes:
29
+ STATUS_OF_CHARGING_NOT_AVAILABLE: Indicates that the status of charging is not available.
30
+ STATUS_OF_CONNECTION_NOT_AVAILABLE: Indicates that the status of connection is not available.
31
+ CARE_MODE_IS_NOT_AVAILABLE: Indicates that the care mode is not available.
32
+ AUTO_UNLOCK_IS_NOT_AVAILABLE: Indicates that the auto unlock feature is not available.
33
+ MAX_CHARGE_CURRENT_IS_NOT_AVAILABLE: Indicates that the maximum charge current setting is not available.
34
+ CHARGE_LIMIT_IS_NOT_AVAILABLE: Indicates that the charge limit setting is not available.
35
+ """
36
+ STATUS_OF_CHARGING_NOT_AVAILABLE = 'STATUS_OF_CHARGING_NOT_AVAILABLE'
37
+ STATUS_OF_CONNECTION_NOT_AVAILABLE = 'STATUS_OF_CONNECTION_NOT_AVAILABLE'
38
+ CARE_MODE_IS_NOT_AVAILABLE = 'CARE_MODE_IS_NOT_AVAILABLE'
39
+ AUTO_UNLOCK_IS_NOT_AVAILABLE = 'AUTO_UNLOCK_IS_NOT_AVAILABLE'
40
+ MAX_CHARGE_CURRENT_IS_NOT_AVAILABLE = 'MAX_CHARGE_CURRENT_IS_NOT_AVAILABLE'
41
+ CHARGE_LIMIT_IS_NOT_AVAILABLE = 'CHARGE_LIMIT_IS_NOT_AVAILABLE'
42
+ UNKNOWN = 'UNKNOWN'
43
+
44
+ class ClimatizationError(Enum):
45
+ """
46
+ ClimatizationError is an enumeration for representing various errors
47
+ related to the climatization system in a Skoda car.
48
+
49
+ This enum can be extended to include specific error codes and messages
50
+ that correspond to different climatization issues.
51
+ """
52
+ UNKNOWN = 'UNKNOWN'
@@ -40,7 +40,6 @@ if TYPE_CHECKING:
40
40
  from carconnectivity_connectors.skoda.connector import Connector
41
41
 
42
42
 
43
-
44
43
  LOG: logging.Logger = logging.getLogger("carconnectivity.connectors.skoda.mqtt")
45
44
  LOG_API: logging.Logger = logging.getLogger("carconnectivity.connectors.skoda-api-debug")
46
45
 
@@ -469,6 +468,15 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
469
468
  if electric_drive is not None:
470
469
  charging_state: Optional[Charging.ChargingState] = vehicle.charging.state.value
471
470
  old_charging_state: Optional[Charging.ChargingState] = charging_state
471
+ if 'mode' in data['data'] and data['data']['mode'] is not None \
472
+ and vehicle.charging is not None and isinstance(vehicle.charging.settings, SkodaCharging.Settings):
473
+ if data['data']['mode'] in SkodaCharging.SkodaChargeMode:
474
+ skoda_charging_mode = SkodaCharging.SkodaChargeMode(data['data']['mode'])
475
+ else:
476
+ LOG_API.info('Unkown charging mode %s not in %s', data['data']['mode'], str(SkodaCharging.SkodaChargeMode))
477
+ skoda_charging_mode = Charging.ChargingState.UNKNOWN
478
+ # pylint: disable-next=protected-access
479
+ vehicle.charging.settings.preferred_charge_mode._set_value(value=skoda_charging_mode, measured=measured_at)
472
480
  if 'state' in data['data'] and data['data']['state'] is not None:
473
481
  if data['data']['state'] in [item.value for item in SkodaCharging.SkodaChargingState]:
474
482
  skoda_charging_state = SkodaCharging.SkodaChargingState(data['data']['state'])
@@ -545,6 +553,10 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
545
553
  """
546
554
  vin = vehicle.id
547
555
  self.delayed_access_function_timers.pop(vin)
556
+ try:
557
+ self._skoda_connector.fetch_vehicle_status(vehicle, no_cache=True)
558
+ except CarConnectivityError as e:
559
+ LOG.error('Error while fetching vehicle status: %s', e)
548
560
  if vehicle.capabilities is not None and vehicle.capabilities.enabled \
549
561
  and vehicle.capabilities.has_capability('CHARGING') and isinstance(vehicle, SkodaElectricVehicle):
550
562
  try:
@@ -569,6 +581,19 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
569
581
  self.delayed_access_function_timers[vin] = threading.Timer(2.0, delayed_access_function, kwargs={'vehicle': vehicle})
570
582
  self.delayed_access_function_timers[vin].start()
571
583
 
584
+ LOG_API.info('Received event name %s service event %s for vehicle %s from user %s: %s', data['name'],
585
+ service_event, vin, user_id, msg.payload)
586
+ return
587
+ elif service_event == 'vehicle-status/lights':
588
+ if 'name' in data and data['name'] == 'change-lights':
589
+ if 'data' in data and data['data'] is not None:
590
+ vehicle: Optional[GenericVehicle] = self._skoda_connector.car_connectivity.garage.get_vehicle(vin)
591
+ if isinstance(vehicle, SkodaVehicle):
592
+ try:
593
+ self._skoda_connector.fetch_vehicle_status(vehicle, no_cache=True)
594
+ except CarConnectivityError as e:
595
+ LOG.error('Error while fetching vehicle status: %s', e)
596
+
572
597
  LOG_API.info('Received event name %s service event %s for vehicle %s from user %s: %s', data['name'],
573
598
  service_event, vin, user_id, msg.payload)
574
599
  return
@@ -582,8 +607,15 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
582
607
  operation_request: str = match.group('operation_request')
583
608
  data: Dict[str, Any] = json.loads(msg.payload)
584
609
  if data is not None:
585
- if operation_request == 'air-conditioning/start-stop-air-conditioning':
586
- vehicle: Optional[GenericVehicle] = self._skoda_connector.car_connectivity.garage.get_vehicle(vin)
610
+ vehicle: Optional[GenericVehicle] = self._skoda_connector.car_connectivity.garage.get_vehicle(vin)
611
+ if operation_request == 'air-conditioning/set-air-conditioning-at-unlock' \
612
+ or operation_request == 'air-conditioning/set-air-conditioning-seats-heating' \
613
+ or operation_request == 'air-conditioning/set-air-conditioning-timers' \
614
+ or operation_request == 'air-conditioning/set-air-conditioning-without-external-power' \
615
+ or operation_request == 'air-conditioning/set-target-temperature' \
616
+ or operation_request == 'air-conditioning/start-stop-air-conditioning' \
617
+ or operation_request == 'air-conditioning/start-stop-window-heating' \
618
+ or operation_request == 'air-conditioning/windows-heating':
587
619
  if isinstance(vehicle, SkodaVehicle):
588
620
  if 'status' in data and data['status'] is not None:
589
621
  if data['status'] == 'COMPLETED_SUCCESS':
@@ -593,6 +625,23 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
593
625
  except CarConnectivityError as e:
594
626
  LOG.error('Error while fetching air-conditioning: %s', e)
595
627
  return
596
- LOG_API.info('Received unknown operation request %s for vehicle %s from user %s: %s', operation_request, vin, user_id, msg.payload)
597
- return
628
+ if operation_request == 'charging/start-stop-charging' \
629
+ or operation_request == 'charging/update-battery-support' \
630
+ or operation_request == 'charging/update-auto-unlock-plug' \
631
+ or operation_request == 'charging/update-care-mode' \
632
+ or operation_request == 'charging/update-charge-limit' \
633
+ or operation_request == 'charging/update-charge-mode' \
634
+ or operation_request == 'charging/update-charging-profiles' \
635
+ or operation_request == 'charging/update-charging-current':
636
+ if isinstance(vehicle, SkodaElectricVehicle):
637
+ if 'status' in data and data['status'] is not None:
638
+ if data['status'] == 'COMPLETED_SUCCESS':
639
+ LOG.debug('Received %s operation request for vehicle %s from user %s', operation_request, vin, user_id)
640
+ try:
641
+ self._skoda_connector.fetch_charging(vehicle, no_cache=True)
642
+ except CarConnectivityError as e:
643
+ LOG.error('Error while fetching charging: %s', e)
644
+ return
645
+ LOG_API.info('Received unknown operation request %s for vehicle %s from user %s: %s', operation_request, vin, user_id, msg.payload)
646
+ return
598
647
  LOG_API.info('I don\'t understand message %s: %s', msg.topic, msg.payload)
@@ -3,8 +3,10 @@ from __future__ import annotations
3
3
  from typing import TYPE_CHECKING
4
4
 
5
5
  from carconnectivity.vehicle import GenericVehicle, ElectricVehicle, CombustionVehicle, HybridVehicle
6
+ from carconnectivity.charging import Charging
6
7
 
7
8
  from carconnectivity_connectors.skoda.capability import Capabilities
9
+ from carconnectivity_connectors.skoda.charging import SkodaCharging
8
10
 
9
11
  if TYPE_CHECKING:
10
12
  from typing import Optional
@@ -39,8 +41,12 @@ class SkodaElectricVehicle(ElectricVehicle, SkodaVehicle):
39
41
  origin: Optional[SkodaVehicle] = None) -> None:
40
42
  if origin is not None:
41
43
  super().__init__(origin=origin)
44
+ if isinstance(origin, ElectricVehicle):
45
+ self.charging: Charging = SkodaCharging(origin=origin.charging)
46
+ self.charging.parent = self
42
47
  else:
43
48
  super().__init__(vin=vin, garage=garage, managing_connector=managing_connector)
49
+ self.charging: Charging = SkodaCharging(vehicle=self)
44
50
 
45
51
 
46
52
  class SkodaCombustionVehicle(CombustionVehicle, SkodaVehicle):
@@ -1,67 +0,0 @@
1
- """
2
- Module for charging for skoda vehicles.
3
- """
4
- from __future__ import annotations
5
- from typing import TYPE_CHECKING
6
-
7
- from enum import Enum
8
-
9
- from carconnectivity.charging import Charging
10
-
11
- if TYPE_CHECKING:
12
- from typing import Dict
13
-
14
-
15
- class SkodaCharging(Charging): # pylint: disable=too-many-instance-attributes
16
- """
17
- SkodaCharging class for handling Skoda vehicle charging information.
18
-
19
- This class extends the Charging class and includes an enumeration of various
20
- charging states specific to Skoda vehicles.
21
- """
22
- class SkodaChargingState(Enum,):
23
- """
24
- Enum representing the various charging states for a Skoda vehicle.
25
-
26
- Attributes:
27
- OFF: The vehicle is not charging.
28
- READY_FOR_CHARGING: The vehicle is ready to start charging.
29
- NOT_READY_FOR_CHARGING: The vehicle is not ready to start charging.
30
- CONSERVATION: The vehicle is in conservation mode.
31
- CHARGE_PURPOSE_REACHED_NOT_CONSERVATION_CHARGING: The vehicle has reached its charging purpose and is not in conservation charging mode.
32
- CHARGE_PURPOSE_REACHED_CONSERVATION: The vehicle has reached its charging purpose and is in conservation charging mode.
33
- CHARGING: The vehicle is currently charging.
34
- ERROR: There is an error in the charging process.
35
- UNSUPPORTED: The charging state is unsupported.
36
- DISCHARGING: The vehicle is discharging.
37
- UNKNOWN: The charging state is unknown.
38
- """
39
- OFF = 'off'
40
- CONNECT_CABLE = 'connectCable'
41
- READY_FOR_CHARGING = 'readyForCharging'
42
- NOT_READY_FOR_CHARGING = 'notReadyForCharging'
43
- CONSERVATION = 'conservation'
44
- CHARGE_PURPOSE_REACHED_NOT_CONSERVATION_CHARGING = 'chargePurposeReachedAndNotConservationCharging'
45
- CHARGE_PURPOSE_REACHED_CONSERVATION = 'chargePurposeReachedAndConservation'
46
- CHARGING = 'charging'
47
- ERROR = 'error'
48
- UNSUPPORTED = 'unsupported'
49
- DISCHARGING = 'discharging'
50
- UNKNOWN = 'unknown charging state'
51
-
52
-
53
- # Mapping of Skoda charging states to generic charging states
54
- mapping_skoda_charging_state: Dict[SkodaCharging.SkodaChargingState, Charging.ChargingState] = {
55
- SkodaCharging.SkodaChargingState.OFF: Charging.ChargingState.OFF,
56
- SkodaCharging.SkodaChargingState.CONNECT_CABLE: Charging.ChargingState.OFF,
57
- SkodaCharging.SkodaChargingState.READY_FOR_CHARGING: Charging.ChargingState.READY_FOR_CHARGING,
58
- SkodaCharging.SkodaChargingState.NOT_READY_FOR_CHARGING: Charging.ChargingState.OFF,
59
- SkodaCharging.SkodaChargingState.CONSERVATION: Charging.ChargingState.CONSERVATION,
60
- SkodaCharging.SkodaChargingState.CHARGE_PURPOSE_REACHED_NOT_CONSERVATION_CHARGING: Charging.ChargingState.READY_FOR_CHARGING,
61
- SkodaCharging.SkodaChargingState.CHARGE_PURPOSE_REACHED_CONSERVATION: Charging.ChargingState.CONSERVATION,
62
- SkodaCharging.SkodaChargingState.CHARGING: Charging.ChargingState.CHARGING,
63
- SkodaCharging.SkodaChargingState.ERROR: Charging.ChargingState.ERROR,
64
- SkodaCharging.SkodaChargingState.UNSUPPORTED: Charging.ChargingState.UNSUPPORTED,
65
- SkodaCharging.SkodaChargingState.DISCHARGING: Charging.ChargingState.DISCHARGING,
66
- SkodaCharging.SkodaChargingState.UNKNOWN: Charging.ChargingState.UNKNOWN
67
- }