carconnectivity-connector-skoda 0.1a21__py3-none-any.whl → 0.2a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: carconnectivity-connector-skoda
3
- Version: 0.1a21
3
+ Version: 0.2a1
4
4
  Summary: CarConnectivity connector for Skoda 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.8
38
38
  Description-Content-Type: text/markdown
39
39
  License-File: LICENSE
40
- Requires-Dist: carconnectivity
40
+ Requires-Dist: carconnectivity>=0.2a1
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,12 +1,13 @@
1
1
  carconnectivity_connectors/skoda/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- carconnectivity_connectors/skoda/_version.py,sha256=-vcE8ACd4f4xkWjtAIGu7OmA4mHDuj0tWUvm5MRRZ38,409
3
- carconnectivity_connectors/skoda/capability.py,sha256=JlNEaisVYF8qWv0wNDHTaas36uIpTIQ3NVR69wesiYQ,4513
2
+ carconnectivity_connectors/skoda/_version.py,sha256=4Y9bnt1x-p71kTX4650igUjzXxyHB_QN6k4UFEycuZQ,408
3
+ carconnectivity_connectors/skoda/capability.py,sha256=SnK9xVsqDA5EcWtBznzfWxe6gIpkYdjgY3UzNIc3OCY,4198
4
4
  carconnectivity_connectors/skoda/charging.py,sha256=rG_GoDPetjyjWCyV6l65hgEDU6ths6MkMQ0KL25rbVU,6663
5
5
  carconnectivity_connectors/skoda/climatization.py,sha256=-Nk4tO5C5_YYNQfUIUWBL7mGgR6-J0_pOZplLK8p_ms,1627
6
- carconnectivity_connectors/skoda/connector.py,sha256=KWQWWz1KIf_Sx2t_wqYCb1yz2FywijI9yXhsWYouLRU,109729
6
+ carconnectivity_connectors/skoda/command_impl.py,sha256=WdgxWPgi82-UgmyFpiSZE-KHRtRjqn7CH-YX9N3bAoI,2875
7
+ carconnectivity_connectors/skoda/connector.py,sha256=2S8DVtk9KX-y9UDRtm9wbtqMUe6JNBe9mn46XsoeceY,105145
7
8
  carconnectivity_connectors/skoda/error.py,sha256=EnzzDxxJ1fswYT5QnMOVSebfoAcqoPmHKcG5i0Tqk3E,2405
8
- carconnectivity_connectors/skoda/mqtt_client.py,sha256=oLmITkh_KKtFyaf2TLxqh2M-nSsKaO0n0t5Vnwd50Sk,37576
9
- carconnectivity_connectors/skoda/vehicle.py,sha256=JKLqb6F4RyJ4C3D_sc6kcsdlqxZSlmdJNQfW04m7u78,3204
9
+ carconnectivity_connectors/skoda/mqtt_client.py,sha256=lfHJfKOl-FBVd5hV6cS6ZMpZ53ktXyVc4lafvQls-Tk,37748
10
+ carconnectivity_connectors/skoda/vehicle.py,sha256=_ALtlBy7sKVHmqpqAhWNbMd9dto915_SdNWcRi_AqYU,3088
10
11
  carconnectivity_connectors/skoda/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
12
  carconnectivity_connectors/skoda/auth/auth_util.py,sha256=dGLUbUre0HBsTg_Ii5vW34f8DLrCykYJYCyzEvUBBEE,4434
12
13
  carconnectivity_connectors/skoda/auth/my_skoda_session.py,sha256=lSh23SFJs8opjmPwHTv-KNIKDep_WY4aItSP4Zq7bT8,10396
@@ -14,8 +15,8 @@ carconnectivity_connectors/skoda/auth/openid_session.py,sha256=LusWi2FZZIL3buodG
14
15
  carconnectivity_connectors/skoda/auth/session_manager.py,sha256=Uf1vujuDBYUCAXhYToOsZkgbTtfmY3Qe0ICTfwomBpI,2899
15
16
  carconnectivity_connectors/skoda/auth/skoda_web_session.py,sha256=cjzMkzx473Sh-4RgZAQULeRRcxB1MboddldCVM_y5LE,10640
16
17
  carconnectivity_connectors/skoda/auth/helpers/blacklist_retry.py,sha256=f3wsiY5bpHDBxp7Va1Mv9nKJ4u3qnCHZZmDu78_AhMk,1251
17
- carconnectivity_connector_skoda-0.1a21.dist-info/LICENSE,sha256=PIwI1alwDyOfvEQHdGCm2u9uf_mGE8030xZDfun0xTo,1071
18
- carconnectivity_connector_skoda-0.1a21.dist-info/METADATA,sha256=bue0YMrsf7JVL00eAshzG1RcLlz1Xh54HZAMz9F2SbE,5327
19
- carconnectivity_connector_skoda-0.1a21.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
20
- carconnectivity_connector_skoda-0.1a21.dist-info/top_level.txt,sha256=KqA8GviZsDH4PtmnwSQsz0HB_w-TWkeEHLIRNo5dTaI,27
21
- carconnectivity_connector_skoda-0.1a21.dist-info/RECORD,,
18
+ carconnectivity_connector_skoda-0.2a1.dist-info/LICENSE,sha256=PIwI1alwDyOfvEQHdGCm2u9uf_mGE8030xZDfun0xTo,1071
19
+ carconnectivity_connector_skoda-0.2a1.dist-info/METADATA,sha256=gd-yKmVlkqZxS3VPnPOVx_ixUTdNWMJw0XpTiqRVtu4,5333
20
+ carconnectivity_connector_skoda-0.2a1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
21
+ carconnectivity_connector_skoda-0.2a1.dist-info/top_level.txt,sha256=KqA8GviZsDH4PtmnwSQsz0HB_w-TWkeEHLIRNo5dTaI,27
22
+ carconnectivity_connector_skoda-0.2a1.dist-info/RECORD,,
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.1a21'
16
- __version_tuple__ = version_tuple = (0, 1)
15
+ __version__ = version = '0.2a1'
16
+ __version_tuple__ = version_tuple = (0, 2)
@@ -89,12 +89,6 @@ class Capabilities(GenericObject):
89
89
  """
90
90
  return capability_id in self.__capabilities
91
91
 
92
- def __str__(self) -> str:
93
- return_string = 'Capabilities:\n'
94
- for capability in self.__capabilities.values():
95
- return_string += f'\t{capability}\n'
96
- return return_string
97
-
98
92
 
99
93
  class Capability(GenericObject):
100
94
  """
@@ -113,10 +107,6 @@ class Capability(GenericObject):
113
107
  self.enabled = True
114
108
  self.delay_notifications = False
115
109
 
116
- def __str__(self) -> str:
117
- return_string = f'{self.capability_id}'
118
- return return_string
119
-
120
110
  class Status(IntEnum):
121
111
  """
122
112
  Enum for capability status.
@@ -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.skoda")
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 @@ import requests
15
15
  from carconnectivity.garage import Garage
16
16
  from carconnectivity.vehicle import GenericVehicle
17
17
  from carconnectivity.errors import AuthenticationError, TooManyRequestsError, RetrievalError, APIError, APICompatibilityError, \
18
- TemporaryAuthenticationError, ConfigurationError, SetterError
18
+ TemporaryAuthenticationError, ConfigurationError, SetterError, CommandError
19
19
  from carconnectivity.util import robust_time_parse, log_extra_keys, config_remove_credentials
20
20
  from carconnectivity.units import Length, Speed, Power, Temperature
21
21
  from carconnectivity.doors import Doors
@@ -27,7 +27,8 @@ from carconnectivity.charging import Charging
27
27
  from carconnectivity.position import Position
28
28
  from carconnectivity.climatization import Climatization
29
29
  from carconnectivity.charging_connector import ChargingConnector
30
- from carconnectivity.command_impl import ClimatizationStartStopCommand, ChargingStartStopCommand
30
+ from carconnectivity.commands import Commands
31
+ from carconnectivity.command_impl import ClimatizationStartStopCommand, ChargingStartStopCommand, HonkAndFlashCommand, LockUnlockCommand, WakeSleepCommand
31
32
 
32
33
  from carconnectivity_connectors.base.connector import BaseConnector
33
34
  from carconnectivity_connectors.skoda.auth.session_manager import SessionManager, SessionUser, Service
@@ -39,6 +40,7 @@ from carconnectivity_connectors.skoda.climatization import SkodaClimatization
39
40
  from carconnectivity_connectors.skoda.error import Error
40
41
  from carconnectivity_connectors.skoda._version import __version__
41
42
  from carconnectivity_connectors.skoda.mqtt_client import SkodaMQTTClient
43
+ from carconnectivity_connectors.skoda.command_impl import SpinCommand
42
44
 
43
45
  if TYPE_CHECKING:
44
46
  from typing import Dict, List, Optional, Any, Set, Union
@@ -69,6 +71,7 @@ class Connector(BaseConnector):
69
71
 
70
72
  self.connected: BooleanAttribute = BooleanAttribute(name="connected", parent=self)
71
73
  self.interval: DurationAttribute = DurationAttribute(name="interval", parent=self)
74
+ self.commands: Commands = Commands(parent=self)
72
75
 
73
76
  self.user_id: Optional[str] = None
74
77
 
@@ -91,6 +94,11 @@ class Connector(BaseConnector):
91
94
  raise ConfigurationError(f'Invalid log level: "{config["log_level"]}" not in {list(logging._nameToLevel.keys())}')
92
95
  LOG.info("Loading skoda connector with config %s", config_remove_credentials(self.config))
93
96
 
97
+ if 'spin' in config and config['spin'] is not None:
98
+ self._spin: Optional[str] = config['spin']
99
+ else:
100
+ self._spin = None
101
+
94
102
  username: Optional[str] = None
95
103
  password: Optional[str] = None
96
104
  if 'username' in self.config and 'password' in self.config:
@@ -106,7 +114,13 @@ class Connector(BaseConnector):
106
114
  secret: tuple[str, str, str] | None = secrets.authenticators("skoda")
107
115
  if secret is None:
108
116
  raise AuthenticationError(f'Authentication using {netrc_filename} failed: skoda not found in netrc')
109
- username, _, password = secret
117
+ username, account, password = secret
118
+
119
+ if self._spin is None and account is not None:
120
+ try:
121
+ self._spin = account
122
+ except ValueError as err:
123
+ LOG.error('Could not parse spin from netrc: %s', err)
110
124
  except netrc.NetrcParseError as err:
111
125
  LOG.error('Authentification using %s failed: %s', netrc_filename, err)
112
126
  raise AuthenticationError(f'Authentication using {netrc_filename} failed: {err}') from err
@@ -241,6 +255,12 @@ class Connector(BaseConnector):
241
255
 
242
256
  This method calls the `fetch_vehicles` method to retrieve vehicle data.
243
257
  """
258
+ # Add spin command
259
+ if self.commands is not None and not self.commands.contains_command('spin'):
260
+ spin_command = SpinCommand(parent=self.commands)
261
+ spin_command._add_on_set_hook(self.__on_spin) # pylint: disable=protected-access
262
+ spin_command.enabled = True
263
+ self.commands.add_command(spin_command)
244
264
  self.fetch_vehicles()
245
265
  self.car_connectivity.transaction_end()
246
266
 
@@ -348,10 +368,11 @@ class Connector(BaseConnector):
348
368
  url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}'
349
369
  data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
350
370
  if data is not None:
351
- start_stop_command: ChargingStartStopCommand = ChargingStartStopCommand(parent=vehicle.charging.commands)
352
- start_stop_command._add_on_set_hook(self.__on_charging_start_stop) # pylint: disable=protected-access
353
- start_stop_command.enabled = True
354
- vehicle.charging.commands.add_command(start_stop_command)
371
+ if not vehicle.climatization.commands.contains_command('start-stop'):
372
+ start_stop_command: ChargingStartStopCommand = ChargingStartStopCommand(parent=vehicle.charging.commands)
373
+ start_stop_command._add_on_set_hook(self.__on_charging_start_stop) # pylint: disable=protected-access
374
+ start_stop_command.enabled = True
375
+ vehicle.charging.commands.add_command(start_stop_command)
355
376
  if 'carCapturedTimestamp' in data and data['carCapturedTimestamp'] is not None:
356
377
  captured_at: datetime = robust_time_parse(data['carCapturedTimestamp'])
357
378
  else:
@@ -872,6 +893,32 @@ class Connector(BaseConnector):
872
893
  else:
873
894
  vehicle.capabilities.clear_capabilities()
874
895
 
896
+ if vehicle.capabilities.has_capability('VEHICLE_WAKE_UP_TRIGGER'):
897
+ if vehicle.commands is not None and vehicle.commands.commands is not None \
898
+ and not vehicle.commands.contains_command('wake-sleep'):
899
+ wake_sleep_command = WakeSleepCommand(parent=vehicle.commands)
900
+ wake_sleep_command._add_on_set_hook(self.__on_wake_sleep) # pylint: disable=protected-access
901
+ wake_sleep_command.enabled = True
902
+ vehicle.commands.add_command(wake_sleep_command)
903
+
904
+ # Add HONK_AND_FLASH command if necessary capabilities are available
905
+ if vehicle.capabilities.has_capability('HONK_AND_FLASH') and vehicle.capabilities.has_capability('PARKING_POSITION'):
906
+ if vehicle.commands is not None and vehicle.commands.commands is not None \
907
+ and not vehicle.commands.contains_command('honk-flash'):
908
+ honk_flash_command = HonkAndFlashCommand(parent=vehicle.commands)
909
+ honk_flash_command._add_on_set_hook(self.__on_honk_flash) # pylint: disable=protected-access
910
+ honk_flash_command.enabled = True
911
+ vehicle.commands.add_command(honk_flash_command)
912
+
913
+ # Add lock and unlock command
914
+ if vehicle.capabilities.has_capability('ACCESS'):
915
+ if vehicle.doors is not None and vehicle.doors.commands is not None and vehicle.doors.commands.commands is not None \
916
+ and not vehicle.doors.commands.contains_command('lock-unlock'):
917
+ lock_unlock_command = LockUnlockCommand(parent=vehicle.doors.commands)
918
+ lock_unlock_command._add_on_set_hook(self.__on_lock_unlock) # pylint: disable=protected-access
919
+ lock_unlock_command.enabled = True
920
+ vehicle.doors.commands.add_command(lock_unlock_command)
921
+
875
922
  if 'specification' in vehicle_data and vehicle_data['specification'] is not None:
876
923
  if 'model' in vehicle_data['specification'] and vehicle_data['specification']['model'] is not None:
877
924
  vehicle.model._set_value(vehicle_data['specification']['model']) # pylint: disable=protected-access
@@ -1087,187 +1134,6 @@ class Connector(BaseConnector):
1087
1134
  log_extra_keys(LOG_API, f'/api/v2/vehicle-status/{vin}', vehicle_status_data, {'overall', 'carCapturedTimestamp'})
1088
1135
  return vehicle
1089
1136
 
1090
- # def fetch_vehicle_status_second_api(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
1091
- # """
1092
- # Fetches the status of a vehicle from other Skoda API.
1093
- #
1094
- # Args:
1095
- # vehicle (GenericVehicle): The vehicle object containing the VIN.
1096
- #
1097
- # Returns:
1098
- # None
1099
- # """
1100
- # vin = vehicle.vin.value
1101
- # if vin is None:
1102
- # raise APIError('VIN is missing')
1103
- # url = f'https://api.connect.skoda-auto.cz/api/v2/vehicle-status/{vin}'
1104
- # vehicle_status_data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
1105
- # if vehicle_status_data:
1106
- # if 'remote' in vehicle_status_data and vehicle_status_data['remote'] is not None:
1107
- # vehicle_status_data = vehicle_status_data['remote']
1108
- # if vehicle_status_data:
1109
- # if 'capturedAt' in vehicle_status_data and vehicle_status_data['capturedAt'] is not None:
1110
- # captured_at: datetime = robust_time_parse(vehicle_status_data['capturedAt'])
1111
- # else:
1112
- # raise APIError('Could not fetch vehicle status, capturedAt missing')
1113
- # if 'mileageInKm' in vehicle_status_data and vehicle_status_data['mileageInKm'] is not None:
1114
- # # pylint: disable-next=protected-access
1115
- # vehicle.odometer._set_value(value=vehicle_status_data['mileageInKm'], measured=captured_at, unit=Length.KM)
1116
- # else:
1117
- # vehicle.odometer._set_value(value=None, measured=captured_at, unit=Length.KM) # pylint: disable=protected-access
1118
- # if 'status' in vehicle_status_data and vehicle_status_data['status'] is not None:
1119
- # if 'open' in vehicle_status_data['status'] and vehicle_status_data['status']['open'] is not None:
1120
- # if vehicle_status_data['status']['open'] == 'YES':
1121
- # vehicle.doors.open_state._set_value(Doors.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
1122
- # elif vehicle_status_data['status']['open'] == 'NO':
1123
- # vehicle.doors.open_state._set_value(Doors.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
1124
- # else:
1125
- # vehicle.doors.open_state._set_value(Doors.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1126
- # LOG_API.info('Unknown door open state: %s', vehicle_status_data['status']['open'])
1127
- # else:
1128
- # vehicle.doors.open_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
1129
- # if 'locked' in vehicle_status_data['status'] and vehicle_status_data['status']['locked'] is not None:
1130
- # if vehicle_status_data['status']['locked'] == 'YES':
1131
- # vehicle.doors.lock_state._set_value(Doors.LockState.LOCKED, measured=captured_at) # pylint: disable=protected-access
1132
- # elif vehicle_status_data['status']['locked'] == 'NO':
1133
- # vehicle.doors.lock_state._set_value(Doors.LockState.UNLOCKED, measured=captured_at) # pylint: disable=protected-access
1134
- # else:
1135
- # vehicle.doors.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1136
- # LOG_API.info('Unknown door lock state: %s', vehicle_status_data['status']['locked'])
1137
- # else:
1138
- # vehicle.doors.lock_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
1139
- # else:
1140
- # vehicle.doors.open_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
1141
- # vehicle.doors.lock_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
1142
- # if 'doors' in vehicle_status_data and vehicle_status_data['doors'] is not None:
1143
- # seen_door_ids: set[str] = set()
1144
- # for door_status in vehicle_status_data['doors']:
1145
- # if 'name' in door_status and door_status['name'] is not None:
1146
- # door_id = door_status['name']
1147
- # seen_door_ids.add(door_id)
1148
- # if door_id in vehicle.doors.doors:
1149
- # door: Doors.Door = vehicle.doors.doors[door_id]
1150
- # else:
1151
- # door = Doors.Door(door_id=door_id, doors=vehicle.doors)
1152
- # vehicle.doors.doors[door_id] = door
1153
- # if 'status' in door_status and door_status['status'] is not None:
1154
- # if door_status['status'] == 'OPEN':
1155
- # door.lock_state._set_value(Doors.LockState.UNLOCKED, measured=captured_at) # pylint: disable=protected-access
1156
- # door.open_state._set_value(Doors.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
1157
- # elif door_status['status'] == 'CLOSED':
1158
- # door.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1159
- # door.open_state._set_value(Doors.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
1160
- # elif door_status['status'] == 'LOCKED':
1161
- # door.lock_state._set_value(Doors.LockState.LOCKED, measured=captured_at) # pylint: disable=protected-access
1162
- # door.open_state._set_value(Doors.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
1163
- # elif door_status['status'] == 'UNSUPPORTED':
1164
- # door.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1165
- # door.open_state._set_value(Doors.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1166
- # else:
1167
- # LOG_API.info('Unknown door status %s', door_status['status'])
1168
- # door.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1169
- # door.open_state._set_value(Doors.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1170
- # else:
1171
- # door.lock_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
1172
- # door.open_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
1173
- # else:
1174
- # raise APIError('Could not parse door, name missing')
1175
- # log_extra_keys(LOG_API, 'doors', door_status, {'name', 'status'})
1176
- # for door_to_remove in set(vehicle.doors.doors) - seen_door_ids:
1177
- # vehicle.doors.doors[door_to_remove].enabled = False
1178
- # vehicle.doors.doors.pop(door_to_remove)
1179
- # log_extra_keys(LOG_API, 'status', vehicle_status_data['status'], {'open', 'locked'})
1180
- # else:
1181
- # vehicle.doors.open_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
1182
- # vehicle.doors.doors = {}
1183
- # if 'windows' in vehicle_status_data and vehicle_status_data['windows'] is not None:
1184
- # seen_window_ids: set[str] = set()
1185
- # all_windows_closed: bool = True
1186
- # for window_status in vehicle_status_data['windows']:
1187
- # if 'name' in window_status and window_status['name'] is not None:
1188
- # window_id = window_status['name']
1189
- # seen_window_ids.add(window_id)
1190
- # if window_id in vehicle.windows.windows:
1191
- # window: Windows.Window = vehicle.windows.windows[window_id]
1192
- # else:
1193
- # window = Windows.Window(window_id=window_id, windows=vehicle.windows)
1194
- # vehicle.windows.windows[window_id] = window
1195
- # if 'status' in window_status and window_status['status'] is not None:
1196
- # if window_status['status'] == 'OPEN':
1197
- # all_windows_closed = False
1198
- # window.open_state._set_value(Windows.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
1199
- # elif window_status['status'] == 'CLOSED':
1200
- # window.open_state._set_value(Windows.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
1201
- # elif window_status['status'] == 'UNSUPPORTED':
1202
- # window.open_state._set_value(Windows.OpenState.UNSUPPORTED, measured=captured_at) # pylint: disable=protected-access
1203
- # elif window_status['status'] == 'INVALID':
1204
- # window.open_state._set_value(Windows.OpenState.INVALID, measured=captured_at) # pylint: disable=protected-access
1205
- # else:
1206
- # LOG_API.info('Unknown window status %s', window_status['status'])
1207
- # window.open_state._set_value(Windows.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1208
- # else:
1209
- # window.open_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
1210
- # else:
1211
- # raise APIError('Could not parse window, name missing')
1212
- # log_extra_keys(LOG_API, 'doors', window_status, {'name', 'status'})
1213
- # for window_to_remove in set(vehicle.windows.windows) - seen_window_ids:
1214
- # vehicle.windows.windows[window_to_remove].enabled = False
1215
- # vehicle.windows.windows.pop(window_to_remove)
1216
- # if all_windows_closed:
1217
- # vehicle.windows.open_state._set_value(Windows.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
1218
- # else:
1219
- # vehicle.windows.open_state._set_value(Windows.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
1220
- # else:
1221
- # vehicle.windows.open_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
1222
- # vehicle.windows.windows = {}
1223
- # if 'lights' in vehicle_status_data and vehicle_status_data['lights'] is not None:
1224
- # seen_light_ids: set[str] = set()
1225
- # if 'overallStatus' in vehicle_status_data['lights'] and vehicle_status_data['lights']['overallStatus'] is not None:
1226
- # if vehicle_status_data['lights']['overallStatus'] == 'ON':
1227
- # vehicle.lights.light_state._set_value(Lights.LightState.ON, measured=captured_at) # pylint: disable=protected-access
1228
- # elif vehicle_status_data['lights']['overallStatus'] == 'OFF':
1229
- # vehicle.lights.light_state._set_value(Lights.LightState.OFF, measured=captured_at) # pylint: disable=protected-access
1230
- # elif vehicle_status_data['lights']['overallStatus'] == 'INVALID':
1231
- # vehicle.lights.light_state._set_value(Lights.LightState.INVALID, measured=captured_at) # pylint: disable=protected-access
1232
- # else:
1233
- # LOG_API.info('Unknown light status %s', vehicle_status_data['lights']['overallStatus'])
1234
- # vehicle.lights.light_state._set_value(Lights.LightState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1235
- # else:
1236
- # vehicle.lights.light_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
1237
- # if 'lightsStatus' in vehicle_status_data['lights'] and vehicle_status_data['lights']['lightsStatus'] is not None:
1238
- # for light_status in vehicle_status_data['lights']['lightsStatus']:
1239
- # if 'name' in light_status and light_status['name'] is not None:
1240
- # light_id: str = light_status['name']
1241
- # seen_light_ids.add(light_id)
1242
- # if light_id in vehicle.lights.lights:
1243
- # light: Lights.Light = vehicle.lights.lights[light_id]
1244
- # else:
1245
- # light = Lights.Light(light_id=light_id, lights=vehicle.lights)
1246
- # vehicle.lights.lights[light_id] = light
1247
- # if 'status' in light_status and light_status['status'] is not None:
1248
- # if light_status['status'] == 'ON':
1249
- # light.light_state._set_value(Lights.LightState.ON, measured=captured_at) # pylint: disable=protected-access
1250
- # elif light_status['status'] == 'OFF':
1251
- # light.light_state._set_value(Lights.LightState.OFF, measured=captured_at) # pylint: disable=protected-access
1252
- # elif light_status['status'] == 'INVALID':
1253
- # light.light_state._set_value(Lights.LightState.INVALID, measured=captured_at) # pylint: disable=protected-access
1254
- # else:
1255
- # LOG_API.info('Unknown light status %s', light_status['status'])
1256
- # light.light_state._set_value(Lights.LightState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
1257
- # else:
1258
- # light.light_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
1259
- # else:
1260
- # raise APIError('Could not parse light, name missing')
1261
- # log_extra_keys(LOG_API, 'lights', light_status, {'name', 'status'})
1262
- # for light_to_remove in set(vehicle.lights.lights) - seen_light_ids:
1263
- # vehicle.lights.lights[light_to_remove].enabled = False
1264
- # vehicle.lights.lights.pop(light_to_remove)
1265
- # else:
1266
- # vehicle.lights.lights = {}
1267
- # log_extra_keys(LOG_API, 'lights', vehicle_status_data['lights'], {'overallStatus', 'lightsStatus'})
1268
- # log_extra_keys(LOG_API, 'vehicles', vehicle_status_data, {'capturedAt', 'mileageInKm', 'status', 'doors', 'windows', 'lights'})
1269
- # return vehicle
1270
-
1271
1137
  def _record_elapsed(self, elapsed: timedelta) -> None:
1272
1138
  """
1273
1139
  Records the elapsed time.
@@ -1404,15 +1270,15 @@ class Connector(BaseConnector):
1404
1270
  -> Union[str, Dict[str, Any]]:
1405
1271
  if start_stop_command.parent is None or start_stop_command.parent.parent is None \
1406
1272
  or start_stop_command.parent.parent.parent is None or not isinstance(start_stop_command.parent.parent.parent, SkodaVehicle):
1407
- raise SetterError('Object hierarchy is not as expected')
1273
+ raise CommandError('Object hierarchy is not as expected')
1408
1274
  if not isinstance(command_arguments, dict):
1409
- raise SetterError('Command arguments are not a dictionary')
1275
+ raise CommandError('Command arguments are not a dictionary')
1410
1276
  vehicle: SkodaVehicle = start_stop_command.parent.parent.parent
1411
1277
  vin: Optional[str] = vehicle.vin.value
1412
1278
  if vin is None:
1413
- raise SetterError('VIN in object hierarchy missing')
1279
+ raise CommandError('VIN in object hierarchy missing')
1414
1280
  if 'command' not in command_arguments:
1415
- raise SetterError('Command argument missing')
1281
+ raise CommandError('Command argument missing')
1416
1282
  command_dict = {}
1417
1283
  if command_arguments['command'] == ClimatizationStartStopCommand.Command.START:
1418
1284
  command_dict['heaterSource'] = 'ELECTRIC'
@@ -1422,7 +1288,7 @@ class Connector(BaseConnector):
1422
1288
  command_dict['targetTemperature']['temperatureValue'] = round(command_arguments['target_temperature'] * 2) / 2
1423
1289
  if 'target_temperature_unit' in command_arguments:
1424
1290
  if not isinstance(command_arguments['target_temperature_unit'], Temperature):
1425
- raise SetterError('Temperature unit is not of type Temperature')
1291
+ raise CommandError('Temperature unit is not of type Temperature')
1426
1292
  if command_arguments['target_temperature_unit'] == Temperature.C:
1427
1293
  command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
1428
1294
  elif command_arguments['target_temperature_unit'] == Temperature.F:
@@ -1430,7 +1296,7 @@ class Connector(BaseConnector):
1430
1296
  elif command_arguments['target_temperature_unit'] == Temperature.K:
1431
1297
  command_dict['targetTemperature']['unitInCar'] = 'KELVIN'
1432
1298
  else:
1433
- raise SetterError(f'Unknown temperature unit {command_arguments['target_temperature_unit']}')
1299
+ raise CommandError(f'Unknown temperature unit {command_arguments['target_temperature_unit']}')
1434
1300
  else:
1435
1301
  command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
1436
1302
  elif start_stop_command.parent is not None and (climatization := start_stop_command.parent.parent) is not None \
@@ -1446,46 +1312,167 @@ class Connector(BaseConnector):
1446
1312
  elif climatization.settings.target_temperature.unit == Temperature.K:
1447
1313
  command_dict['targetTemperature']['unitInCar'] = 'KELVIN'
1448
1314
  else:
1449
- raise SetterError(f'Unknown temperature unit {climatization.settings.target_temperature.unit}')
1315
+ raise CommandError(f'Unknown temperature unit {climatization.settings.target_temperature.unit}')
1450
1316
  else:
1451
1317
  command_dict['targetTemperature']['temperatureValue'] = 25.0
1452
1318
  command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
1453
1319
  url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/start'
1454
- settings_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
1320
+ command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
1455
1321
  elif command_arguments['command'] == ClimatizationStartStopCommand.Command.STOP:
1456
1322
  url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/stop'
1457
- settings_response: requests.Response = self.session.post(url, allow_redirects=True)
1323
+ command_response: requests.Response = self.session.post(url, allow_redirects=True)
1458
1324
  else:
1459
- raise SetterError(f'Unknown command {command_arguments["command"]}')
1325
+ raise CommandError(f'Unknown command {command_arguments["command"]}')
1460
1326
 
1461
- if settings_response.status_code != requests.codes['accepted']:
1462
- LOG.error('Could not start/stop air conditioning (%s: %s)', settings_response.status_code, settings_response.text)
1463
- raise SetterError(f'Could not start/stop air conditioning ({settings_response.status_code}: {settings_response.text})')
1327
+ if command_response.status_code != requests.codes['accepted']:
1328
+ LOG.error('Could not start/stop air conditioning (%s: %s)', command_response.status_code, command_response.text)
1329
+ raise CommandError(f'Could not start/stop air conditioning ({command_response.status_code}: {command_response.text})')
1464
1330
  return command_arguments
1465
1331
 
1466
1332
  def __on_charging_start_stop(self, start_stop_command: ChargingStartStopCommand, command_arguments: Union[str, Dict[str, Any]]) \
1467
1333
  -> Union[str, Dict[str, Any]]:
1468
1334
  if start_stop_command.parent is None or start_stop_command.parent.parent is None \
1469
1335
  or start_stop_command.parent.parent.parent is None or not isinstance(start_stop_command.parent.parent.parent, SkodaVehicle):
1470
- raise SetterError('Object hierarchy is not as expected')
1336
+ raise CommandError('Object hierarchy is not as expected')
1471
1337
  if not isinstance(command_arguments, dict):
1472
- raise SetterError('Command arguments are not a dictionary')
1338
+ raise CommandError('Command arguments are not a dictionary')
1473
1339
  vehicle: SkodaVehicle = start_stop_command.parent.parent.parent
1474
1340
  vin: Optional[str] = vehicle.vin.value
1475
1341
  if vin is None:
1476
- raise SetterError('VIN in object hierarchy missing')
1342
+ raise CommandError('VIN in object hierarchy missing')
1477
1343
  if 'command' not in command_arguments:
1478
- raise SetterError('Command argument missing')
1344
+ raise CommandError('Command argument missing')
1479
1345
  if command_arguments['command'] == ChargingStartStopCommand.Command.START:
1480
1346
  url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}/start'
1481
- settings_response: requests.Response = self.session.post(url, allow_redirects=True)
1347
+ command_response: requests.Response = self.session.post(url, allow_redirects=True)
1482
1348
  elif command_arguments['command'] == ChargingStartStopCommand.Command.STOP:
1483
1349
  url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}/stop'
1484
- settings_response: requests.Response = self.session.post(url, allow_redirects=True)
1350
+ command_response: requests.Response = self.session.post(url, allow_redirects=True)
1485
1351
  else:
1486
- raise SetterError(f'Unknown command {command_arguments["command"]}')
1352
+ raise CommandError(f'Unknown command {command_arguments["command"]}')
1487
1353
 
1488
- if settings_response.status_code != requests.codes['accepted']:
1489
- LOG.error('Could not start/stop charging (%s: %s)', settings_response.status_code, settings_response.text)
1490
- raise SetterError(f'Could not start/stop charging ({settings_response.status_code}: {settings_response.text})')
1354
+ if command_response.status_code != requests.codes['accepted']:
1355
+ LOG.error('Could not start/stop charging (%s: %s)', command_response.status_code, command_response.text)
1356
+ raise CommandError(f'Could not start/stop charging ({command_response.status_code}: {command_response.text})')
1357
+ return command_arguments
1358
+
1359
+ def __on_honk_flash(self, honk_flash_command: HonkAndFlashCommand, command_arguments: Union[str, Dict[str, Any]]) \
1360
+ -> Union[str, Dict[str, Any]]:
1361
+ if honk_flash_command.parent is None or honk_flash_command.parent.parent is None \
1362
+ or not isinstance(honk_flash_command.parent.parent, SkodaVehicle):
1363
+ raise CommandError('Object hierarchy is not as expected')
1364
+ if not isinstance(command_arguments, dict):
1365
+ raise CommandError('Command arguments are not a dictionary')
1366
+ vehicle: SkodaVehicle = honk_flash_command.parent.parent
1367
+ vin: Optional[str] = vehicle.vin.value
1368
+ if vin is None:
1369
+ raise CommandError('VIN in object hierarchy missing')
1370
+ if 'command' not in command_arguments:
1371
+ raise CommandError('Command argument missing')
1372
+ if 'duration' in command_arguments:
1373
+ LOG.warning('Duration argument is not supported by the Skoda API')
1374
+ command_dict = {}
1375
+ if command_arguments['command'] in [HonkAndFlashCommand.Command.FLASH, HonkAndFlashCommand.Command.HONK_AND_FLASH]:
1376
+ command_dict['mode'] = command_arguments['command'].name
1377
+ command_dict['vehiclePosition'] = {}
1378
+ if vehicle.position is None or vehicle.position.latitude is None or vehicle.position.longitude is None \
1379
+ or vehicle.position.latitude.value is None or vehicle.position.longitude.value is None \
1380
+ or not vehicle.position.latitude.enabled or not vehicle.position.longitude.enabled:
1381
+ raise CommandError('Can only execute honk and flash commands if vehicle position is known')
1382
+ command_dict['vehiclePosition']['latitude'] = vehicle.position.latitude.value
1383
+ command_dict['vehiclePosition']['longitude'] = vehicle.position.longitude.value
1384
+
1385
+ url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/vehicle-access/{vin}/honk-and-flash'
1386
+ command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
1387
+ if command_response.status_code != requests.codes['accepted']:
1388
+ LOG.error('Could not execute honk or flash command (%s: %s)', command_response.status_code, command_response.text)
1389
+ raise CommandError(f'Could not execute honk or flash command ({command_response.status_code}: {command_response.text})')
1390
+ else:
1391
+ raise CommandError(f'Unknown command {command_arguments["command"]}')
1392
+ return command_arguments
1393
+
1394
+ def __on_lock_unlock(self, lock_unlock_command: LockUnlockCommand, command_arguments: Union[str, Dict[str, Any]]) \
1395
+ -> Union[str, Dict[str, Any]]:
1396
+ if lock_unlock_command.parent is None or lock_unlock_command.parent.parent is None \
1397
+ or lock_unlock_command.parent.parent.parent is None or not isinstance(lock_unlock_command.parent.parent.parent, SkodaVehicle):
1398
+ raise CommandError('Object hierarchy is not as expected')
1399
+ if not isinstance(command_arguments, dict):
1400
+ raise SetterError('Command arguments are not a dictionary')
1401
+ vehicle: SkodaVehicle = lock_unlock_command.parent.parent.parent
1402
+ vin: Optional[str] = vehicle.vin.value
1403
+ if vin is None:
1404
+ raise CommandError('VIN in object hierarchy missing')
1405
+ if 'command' not in command_arguments:
1406
+ raise CommandError('Command argument missing')
1407
+ command_dict = {}
1408
+ if 'spin' in command_arguments:
1409
+ command_dict['currentSpin'] = command_arguments['spin']
1410
+ else:
1411
+ if self._spin is None:
1412
+ raise CommandError('S-PIN is missing, please add S-PIN to your configuration or .netrc file')
1413
+ command_dict['currentSpin'] = self._spin
1414
+ if command_arguments['command'] == LockUnlockCommand.Command.LOCK:
1415
+ url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/vehicle-access/{vin}/lock'
1416
+ elif command_arguments['command'] == LockUnlockCommand.Command.UNLOCK:
1417
+ url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/vehicle-access/{vin}/unlock'
1418
+ else:
1419
+ raise CommandError(f'Unknown command {command_arguments["command"]}')
1420
+ command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
1421
+ if command_response.status_code != requests.codes['accepted']:
1422
+ LOG.error('Could not execute locking command (%s: %s)', command_response.status_code, command_response.text)
1423
+ raise CommandError(f'Could not execute locking command ({command_response.status_code}: {command_response.text})')
1424
+ return command_arguments
1425
+
1426
+ def __on_wake_sleep(self, wake_sleep_command: WakeSleepCommand, command_arguments: Union[str, Dict[str, Any]]) \
1427
+ -> Union[str, Dict[str, Any]]:
1428
+ if wake_sleep_command.parent is None or wake_sleep_command.parent.parent is None \
1429
+ or not isinstance(wake_sleep_command.parent.parent, GenericVehicle):
1430
+ raise CommandError('Object hierarchy is not as expected')
1431
+ if not isinstance(command_arguments, dict):
1432
+ raise CommandError('Command arguments are not a dictionary')
1433
+ vehicle: GenericVehicle = wake_sleep_command.parent.parent
1434
+ vin: Optional[str] = vehicle.vin.value
1435
+ if vin is None:
1436
+ raise CommandError('VIN in object hierarchy missing')
1437
+ if 'command' not in command_arguments:
1438
+ raise CommandError('Command argument missing')
1439
+ if command_arguments['command'] == WakeSleepCommand.Command.WAKE:
1440
+ url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/vehicle-wakeup/{vin}?applyRequestLimiter=true'
1441
+
1442
+ command_response: requests.Response = self.session.post(url, data='{}', allow_redirects=True)
1443
+ if command_response.status_code != requests.codes['accepted']:
1444
+ LOG.error('Could not execute wake command (%s: %s)', command_response.status_code, command_response.text)
1445
+ raise CommandError(f'Could not execute wake command ({command_response.status_code}: {command_response.text})')
1446
+ elif command_arguments['command'] == WakeSleepCommand.Command.SLEEP:
1447
+ raise CommandError('Sleep command not supported by vehicle. Vehicle will put itself to sleep')
1448
+ else:
1449
+ raise CommandError(f'Unknown command {command_arguments["command"]}')
1450
+ return command_arguments
1451
+
1452
+ def __on_spin(self, spin_command: SpinCommand, command_arguments: Union[str, Dict[str, Any]]) \
1453
+ -> Union[str, Dict[str, Any]]:
1454
+ del spin_command
1455
+ if not isinstance(command_arguments, dict):
1456
+ raise CommandError('Command arguments are not a dictionary')
1457
+ if 'command' not in command_arguments:
1458
+ raise CommandError('Command argument missing')
1459
+ command_dict = {}
1460
+ if self._spin is None:
1461
+ raise CommandError('S-PIN is missing, please add S-PIN to your configuration or .netrc file')
1462
+ if 'spin' in command_arguments:
1463
+ command_dict['currentSpin'] = command_arguments['spin']
1464
+ else:
1465
+ if self._spin is None or self._spin == '':
1466
+ raise CommandError('S-PIN is missing, please add S-PIN to your configuration or .netrc file')
1467
+ command_dict['currentSpin'] = self._spin
1468
+ if command_arguments['command'] == SpinCommand.Command.VERIFY:
1469
+ url = 'https://mysmob.api.connect.skoda-auto.cz/api/v1/spin/verify'
1470
+ else:
1471
+ raise CommandError(f'Unknown command {command_arguments["command"]}')
1472
+ command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
1473
+ if command_response.status_code != requests.codes['ok']:
1474
+ LOG.error('Could not execute spin command (%s: %s)', command_response.status_code, command_response.text)
1475
+ raise CommandError(f'Could not execute spin command ({command_response.status_code}: {command_response.text})')
1476
+ else:
1477
+ LOG.info('Spin verify command executed successfully')
1491
1478
  return command_arguments
@@ -494,6 +494,8 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
494
494
  # pylint: disable-next=protected-access
495
495
  vehicle.charging.power._set_value(value=0, measured=measured_at, unit=Power.KW)
496
496
  if 'soc' in data['data'] and data['data']['soc'] is not None:
497
+ if isinstance(data['data']['soc'], str):
498
+ data['data']['soc'] = int(data['data']['soc'])
497
499
  electric_drive.level._set_value(measured=measured_at, value=data['data']['soc']) # pylint: disable=protected-access
498
500
  if 'chargedRange' in data['data'] and data['data']['chargedRange'] is not None:
499
501
  # pylint: disable-next=protected-access
@@ -29,10 +29,6 @@ class SkodaVehicle(GenericVehicle): # pylint: disable=too-many-instance-attribu
29
29
  self.capabilities = Capabilities(vehicle=self)
30
30
  self.manufacturer._set_value(value='Škoda') # pylint: disable=protected-access
31
31
 
32
- def __str__(self) -> str:
33
- return_string: str = f'\t{self.capabilities}\n'
34
- return return_string
35
-
36
32
 
37
33
  class SkodaElectricVehicle(ElectricVehicle, SkodaVehicle):
38
34
  """