carconnectivity-connector-skoda 0.1a21__py3-none-any.whl → 0.1a22__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.1a22
4
4
  Summary: CarConnectivity connector for Skoda services
5
5
  Author: Till Steinbach
6
6
  License: MIT License
@@ -1,11 +1,12 @@
1
1
  carconnectivity_connectors/skoda/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- carconnectivity_connectors/skoda/_version.py,sha256=-vcE8ACd4f4xkWjtAIGu7OmA4mHDuj0tWUvm5MRRZ38,409
2
+ carconnectivity_connectors/skoda/_version.py,sha256=HEdZtBITW7HcUEt9NLSPGn_UorBnRBrHZWl8fmngNs8,409
3
3
  carconnectivity_connectors/skoda/capability.py,sha256=JlNEaisVYF8qWv0wNDHTaas36uIpTIQ3NVR69wesiYQ,4513
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=dtJsiuhwSuR1PXz7AMrdBowHqPxw6DKSAQKk4Z63GG8,2957
7
+ carconnectivity_connectors/skoda/connector.py,sha256=rMHAigH9t1P95vGewKxK54umBuqkI6-zFQYw5iPeols,118916
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/mqtt_client.py,sha256=lfHJfKOl-FBVd5hV6cS6ZMpZ53ktXyVc4lafvQls-Tk,37748
9
10
  carconnectivity_connectors/skoda/vehicle.py,sha256=JKLqb6F4RyJ4C3D_sc6kcsdlqxZSlmdJNQfW04m7u78,3204
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
@@ -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.1a22.dist-info/LICENSE,sha256=PIwI1alwDyOfvEQHdGCm2u9uf_mGE8030xZDfun0xTo,1071
19
+ carconnectivity_connector_skoda-0.1a22.dist-info/METADATA,sha256=d5o6lGEA7DgbRIAn0be_LDLiLLXOIOmkGRcAdwodadw,5327
20
+ carconnectivity_connector_skoda-0.1a22.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
21
+ carconnectivity_connector_skoda-0.1a22.dist-info/top_level.txt,sha256=KqA8GviZsDH4PtmnwSQsz0HB_w-TWkeEHLIRNo5dTaI,27
22
+ carconnectivity_connector_skoda-0.1a22.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'
15
+ __version__ = version = '0.1a22'
16
16
  __version_tuple__ = version_tuple = (0, 1)
@@ -0,0 +1,74 @@
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")
18
+
19
+
20
+ class SpinCommand(GenericCommand):
21
+ """
22
+ LockUnlockCommand is a command class for locking and unlocking the vehicle.
23
+
24
+ Command (Enum): Enum class representing different commands for locking.
25
+
26
+ """
27
+ def __init__(self, name: str = 'spin', parent: Optional[GenericObject] = None) -> None:
28
+ super().__init__(name=name, parent=parent)
29
+
30
+ @property
31
+ def value(self) -> Optional[Union[str, Dict]]:
32
+ return super().value
33
+
34
+ @value.setter
35
+ def value(self, new_value: Optional[Union[str, Dict]]) -> None:
36
+ if isinstance(new_value, str):
37
+ parser = ThrowingArgumentParser(prog='', add_help=False, exit_on_error=False)
38
+ parser.add_argument('command', help='Command to execute', type=SpinCommand.Command,
39
+ choices=list(SpinCommand.Command))
40
+ parser.add_argument('--spin', dest='spin', help='Spin to be used instead of spin from config or .netrc', type=str, required=False,
41
+ default=None)
42
+ try:
43
+ args = parser.parse_args(new_value.split(sep=' '))
44
+ except argparse.ArgumentError as e:
45
+ raise SetterError(f'Invalid format for SpinCommand: {e.message} {parser.format_usage()}') from e
46
+
47
+ newvalue_dict = {}
48
+ newvalue_dict['command'] = args.command
49
+ if args.spin is not None:
50
+ newvalue_dict['spin'] = args.spin
51
+ new_value = newvalue_dict
52
+ elif isinstance(new_value, dict):
53
+ if 'command' in new_value and isinstance(new_value['command'], str):
54
+ if new_value['command'] in SpinCommand.Command:
55
+ new_value['command'] = SpinCommand.Command(new_value['command'])
56
+ else:
57
+ raise ValueError('Invalid value for SpinCommand. '
58
+ f'Command must be one of {SpinCommand.Command}')
59
+ if self._is_changeable:
60
+ for hook in self._on_set_hooks:
61
+ new_value = hook(self, new_value)
62
+ self._set_value(new_value)
63
+ else:
64
+ raise TypeError('You cannot set this attribute. Attribute is not mutable.')
65
+
66
+ class Command(Enum):
67
+ """
68
+ Enum class representing different commands for SPIN.
69
+
70
+ """
71
+ VERIFY = 'verify'
72
+
73
+ def __str__(self) -> str:
74
+ return self.value
@@ -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
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
@@ -348,10 +362,11 @@ class Connector(BaseConnector):
348
362
  url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}'
349
363
  data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
350
364
  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)
365
+ if not vehicle.climatization.commands.contains_command('start-stop'):
366
+ start_stop_command: ChargingStartStopCommand = ChargingStartStopCommand(parent=vehicle.charging.commands)
367
+ start_stop_command._add_on_set_hook(self.__on_charging_start_stop) # pylint: disable=protected-access
368
+ start_stop_command.enabled = True
369
+ vehicle.charging.commands.add_command(start_stop_command)
355
370
  if 'carCapturedTimestamp' in data and data['carCapturedTimestamp'] is not None:
356
371
  captured_at: datetime = robust_time_parse(data['carCapturedTimestamp'])
357
372
  else:
@@ -872,6 +887,32 @@ class Connector(BaseConnector):
872
887
  else:
873
888
  vehicle.capabilities.clear_capabilities()
874
889
 
890
+ # Add HONK_AND_FLASH command if necessary capabilities are available
891
+ if vehicle.capabilities.has_capability('HONK_AND_FLASH') and vehicle.capabilities.has_capability('PARKING_POSITION'):
892
+ if vehicle.commands is not None and vehicle.commands.commands is not None \
893
+ and not vehicle.commands.contains_command('honk-flash'):
894
+ honk_flash_command = HonkAndFlashCommand(parent=vehicle.commands)
895
+ honk_flash_command._add_on_set_hook(self.__on_honk_flash) # pylint: disable=protected-access
896
+ honk_flash_command.enabled = True
897
+ vehicle.commands.add_command(honk_flash_command)
898
+
899
+ # Add lock and unlock command
900
+ if vehicle.capabilities.has_capability('ACCESS'):
901
+ if vehicle.doors is not None and vehicle.doors.commands is not None and vehicle.doors.commands.commands is not None \
902
+ and not vehicle.doors.commands.contains_command('lock-unlock'):
903
+ lock_unlock_command = LockUnlockCommand(parent=vehicle.doors.commands)
904
+ lock_unlock_command._add_on_set_hook(self.__on_lock_unlock) # pylint: disable=protected-access
905
+ lock_unlock_command.enabled = True
906
+ vehicle.doors.commands.add_command(lock_unlock_command)
907
+
908
+ # Add spin command
909
+ if vehicle.commands is not None and vehicle.commands.commands is not None \
910
+ and not vehicle.commands.contains_command('spin'):
911
+ spin_command = SpinCommand(parent=self.commands)
912
+ spin_command._add_on_set_hook(self.__on_spin) # pylint: disable=protected-access
913
+ spin_command.enabled = True
914
+ vehicle.commands.add_command(spin_command)
915
+
875
916
  if 'specification' in vehicle_data and vehicle_data['specification'] is not None:
876
917
  if 'model' in vehicle_data['specification'] and vehicle_data['specification']['model'] is not None:
877
918
  vehicle.model._set_value(vehicle_data['specification']['model']) # pylint: disable=protected-access
@@ -1451,16 +1492,16 @@ class Connector(BaseConnector):
1451
1492
  command_dict['targetTemperature']['temperatureValue'] = 25.0
1452
1493
  command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
1453
1494
  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)
1495
+ command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
1455
1496
  elif command_arguments['command'] == ClimatizationStartStopCommand.Command.STOP:
1456
1497
  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)
1498
+ command_response: requests.Response = self.session.post(url, allow_redirects=True)
1458
1499
  else:
1459
1500
  raise SetterError(f'Unknown command {command_arguments["command"]}')
1460
1501
 
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})')
1502
+ if command_response.status_code != requests.codes['accepted']:
1503
+ LOG.error('Could not start/stop air conditioning (%s: %s)', command_response.status_code, command_response.text)
1504
+ raise SetterError(f'Could not start/stop air conditioning ({command_response.status_code}: {command_response.text})')
1464
1505
  return command_arguments
1465
1506
 
1466
1507
  def __on_charging_start_stop(self, start_stop_command: ChargingStartStopCommand, command_arguments: Union[str, Dict[str, Any]]) \
@@ -1478,14 +1519,112 @@ class Connector(BaseConnector):
1478
1519
  raise SetterError('Command argument missing')
1479
1520
  if command_arguments['command'] == ChargingStartStopCommand.Command.START:
1480
1521
  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)
1522
+ command_response: requests.Response = self.session.post(url, allow_redirects=True)
1482
1523
  elif command_arguments['command'] == ChargingStartStopCommand.Command.STOP:
1483
1524
  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)
1525
+ command_response: requests.Response = self.session.post(url, allow_redirects=True)
1485
1526
  else:
1486
1527
  raise SetterError(f'Unknown command {command_arguments["command"]}')
1487
1528
 
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})')
1529
+ if command_response.status_code != requests.codes['accepted']:
1530
+ LOG.error('Could not start/stop charging (%s: %s)', command_response.status_code, command_response.text)
1531
+ raise SetterError(f'Could not start/stop charging ({command_response.status_code}: {command_response.text})')
1532
+ return command_arguments
1533
+
1534
+ def __on_honk_flash(self, honk_flash_command: HonkAndFlashCommand, command_arguments: Union[str, Dict[str, Any]]) \
1535
+ -> Union[str, Dict[str, Any]]:
1536
+ if honk_flash_command.parent is None or honk_flash_command.parent.parent is None \
1537
+ or not isinstance(honk_flash_command.parent.parent, SkodaVehicle):
1538
+ raise SetterError('Object hierarchy is not as expected')
1539
+ if not isinstance(command_arguments, dict):
1540
+ raise SetterError('Command arguments are not a dictionary')
1541
+ vehicle: SkodaVehicle = honk_flash_command.parent.parent
1542
+ vin: Optional[str] = vehicle.vin.value
1543
+ if vin is None:
1544
+ raise SetterError('VIN in object hierarchy missing')
1545
+ if 'command' not in command_arguments:
1546
+ raise SetterError('Command argument missing')
1547
+ command_dict = {}
1548
+ if command_arguments['command'] in [HonkAndFlashCommand.Command.FLASH, HonkAndFlashCommand.Command.HONK_AND_FLASH]:
1549
+ command_dict['mode'] = command_arguments['command'].name
1550
+ command_dict['vehiclePosition'] = {}
1551
+ if vehicle.position is None or vehicle.position.latitude is None or vehicle.position.longitude is None \
1552
+ or vehicle.position.latitude.value is None or vehicle.position.longitude.value is None \
1553
+ or not vehicle.position.latitude.enabled or not vehicle.position.longitude.enabled:
1554
+ raise SetterError('Can only execute honk and flash commands if vehicle position is known')
1555
+ command_dict['vehiclePosition']['latitude'] = vehicle.position.latitude.value
1556
+ command_dict['vehiclePosition']['longitude'] = vehicle.position.longitude.value
1557
+
1558
+ url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/vehicle-access/{vin}/honk-and-flash'
1559
+ command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
1560
+ if command_response.status_code != requests.codes['accepted']:
1561
+ LOG.error('Could not execute honk or flash command (%s: %s)', command_response.status_code, command_response.text)
1562
+ raise SetterError(f'Could not execute honk or flash command ({command_response.status_code}: {command_response.text})')
1563
+ else:
1564
+ raise SetterError(f'Unknown command {command_arguments["command"]}')
1565
+ return command_arguments
1566
+
1567
+ def __on_lock_unlock(self, lock_unlock_command: LockUnlockCommand, command_arguments: Union[str, Dict[str, Any]]) \
1568
+ -> Union[str, Dict[str, Any]]:
1569
+ if lock_unlock_command.parent is None or lock_unlock_command.parent.parent is None \
1570
+ or lock_unlock_command.parent.parent.parent is None or not isinstance(lock_unlock_command.parent.parent.parent, SkodaVehicle):
1571
+ raise SetterError('Object hierarchy is not as expected')
1572
+ if not isinstance(command_arguments, dict):
1573
+ raise SetterError('Command arguments are not a dictionary')
1574
+ vehicle: SkodaVehicle = lock_unlock_command.parent.parent.parent
1575
+ vin: Optional[str] = vehicle.vin.value
1576
+ if vin is None:
1577
+ raise SetterError('VIN in object hierarchy missing')
1578
+ if 'command' not in command_arguments:
1579
+ raise SetterError('Command argument missing')
1580
+ command_dict = {}
1581
+ if 'spin' in command_arguments:
1582
+ command_dict['currentSpin'] = command_arguments['spin']
1583
+ else:
1584
+ if self._spin is None:
1585
+ raise SetterError('S-PIN is missing, please add S-PIN to your configuration or .netrc file')
1586
+ command_dict['currentSpin'] = self._spin
1587
+ if command_arguments['command'] == LockUnlockCommand.Command.LOCK:
1588
+ url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/vehicle-access/{vin}/lock'
1589
+ elif command_arguments['command'] == LockUnlockCommand.Command.UNLOCK:
1590
+ url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/vehicle-access/{vin}/unlock'
1591
+ else:
1592
+ raise SetterError(f'Unknown command {command_arguments["command"]}')
1593
+ command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
1594
+ if command_response.status_code != requests.codes['accepted']:
1595
+ LOG.error('Could not execute locking command (%s: %s)', command_response.status_code, command_response.text)
1596
+ raise SetterError(f'Could not execute locking command ({command_response.status_code}: {command_response.text})')
1597
+ return command_arguments
1598
+
1599
+ def __on_spin(self, spin_command: SpinCommand, command_arguments: Union[str, Dict[str, Any]]) \
1600
+ -> Union[str, Dict[str, Any]]:
1601
+ if spin_command.parent is None or spin_command.parent.parent is None or not isinstance(spin_command.parent.parent, SkodaVehicle):
1602
+ raise SetterError('Object hierarchy is not as expected')
1603
+ if not isinstance(command_arguments, dict):
1604
+ raise SetterError('Command arguments are not a dictionary')
1605
+ vehicle: SkodaVehicle = spin_command.parent.parent
1606
+ vin: Optional[str] = vehicle.vin.value
1607
+ if vin is None:
1608
+ raise SetterError('VIN in object hierarchy missing')
1609
+ if 'command' not in command_arguments:
1610
+ raise SetterError('Command argument missing')
1611
+ command_dict = {}
1612
+ if self._spin is None:
1613
+ raise SetterError('S-PIN is missing, please add S-PIN to your configuration or .netrc file')
1614
+ if 'spin' in command_arguments:
1615
+ command_dict['currentSpin'] = command_arguments['spin']
1616
+ else:
1617
+ if self._spin is None:
1618
+ raise SetterError('S-PIN is missing, please add S-PIN to your configuration or .netrc file')
1619
+ command_dict['currentSpin'] = self._spin
1620
+ if command_arguments['command'] == SpinCommand.Command.VERIFY:
1621
+ url = 'https://mysmob.api.connect.skoda-auto.cz/v1/spin/verify'
1622
+ else:
1623
+ raise SetterError(f'Unknown command {command_arguments["command"]}')
1624
+ command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
1625
+ if command_response.status_code != requests.codes['accepted']:
1626
+ LOG.error('Could not execute spin command (%s: %s)', command_response.status_code, command_response.text)
1627
+ raise SetterError(f'Could not execute spin command ({command_response.status_code}: {command_response.text})')
1628
+ else:
1629
+ LOG.info('Spin verify command executed successfully')
1491
1630
  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