carconnectivity-connector-skoda 0.4a3__tar.gz → 0.4a5__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 (41) hide show
  1. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/PKG-INFO +2 -2
  2. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/pyproject.toml +1 -1
  3. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connector_skoda.egg-info/PKG-INFO +2 -2
  4. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connector_skoda.egg-info/requires.txt +1 -1
  5. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/_version.py +1 -1
  6. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/auth/skoda_web_session.py +2 -0
  7. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/connector.py +98 -4
  8. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/vehicle.py +4 -0
  9. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/.flake8 +0 -0
  10. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  11. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  12. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/.github/dependabot.yml +0 -0
  13. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/.github/workflows/build.yml +0 -0
  14. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/.github/workflows/build_and_publish.yml +0 -0
  15. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/.github/workflows/codeql-analysis.yml +0 -0
  16. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/.gitignore +0 -0
  17. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/CHANGELOG.md +0 -0
  18. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/LICENSE +0 -0
  19. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/Makefile +0 -0
  20. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/README.md +0 -0
  21. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/doc/Config.md +0 -0
  22. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/setup.cfg +0 -0
  23. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/setup_requirements.txt +0 -0
  24. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connector_skoda.egg-info/SOURCES.txt +0 -0
  25. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connector_skoda.egg-info/dependency_links.txt +0 -0
  26. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connector_skoda.egg-info/top_level.txt +0 -0
  27. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/__init__.py +0 -0
  28. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/auth/__init__.py +0 -0
  29. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/auth/auth_util.py +0 -0
  30. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/auth/helpers/blacklist_retry.py +0 -0
  31. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/auth/my_skoda_session.py +0 -0
  32. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/auth/openid_session.py +0 -0
  33. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/auth/session_manager.py +0 -0
  34. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/capability.py +0 -0
  35. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/charging.py +0 -0
  36. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/climatization.py +0 -0
  37. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/command_impl.py +0 -0
  38. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/error.py +0 -0
  39. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/mqtt_client.py +0 -0
  40. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/src/carconnectivity_connectors/skoda/ui/connector_ui.py +0 -0
  41. {carconnectivity_connector_skoda-0.4a3 → carconnectivity_connector_skoda-0.4a5}/test/integration_test/carConnectivity.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: carconnectivity-connector-skoda
3
- Version: 0.4a3
3
+ Version: 0.4a5
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>=0.4a4
40
+ Requires-Dist: carconnectivity>=0.4a8
41
41
  Requires-Dist: oauthlib~=3.2.2
42
42
  Requires-Dist: requests~=2.32.3
43
43
  Requires-Dist: jwt~=1.3.1
@@ -14,7 +14,7 @@ authors = [
14
14
  { name = "Till Steinbach" }
15
15
  ]
16
16
  dependencies = [
17
- "carconnectivity>=0.4a4",
17
+ "carconnectivity>=0.4a8",
18
18
  "oauthlib~=3.2.2",
19
19
  "requests~=2.32.3",
20
20
  "jwt~=1.3.1",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: carconnectivity-connector-skoda
3
- Version: 0.4a3
3
+ Version: 0.4a5
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>=0.4a4
40
+ Requires-Dist: carconnectivity>=0.4a8
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,4 +1,4 @@
1
- carconnectivity>=0.4a4
1
+ carconnectivity>=0.4a8
2
2
  oauthlib~=3.2.2
3
3
  requests~=2.32.3
4
4
  jwt~=1.3.1
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.4a3'
20
+ __version__ = version = '0.4a5'
21
21
  __version_tuple__ = version_tuple = (0, 4)
@@ -121,6 +121,8 @@ class SkodaWebSession(OpenIDSession):
121
121
  raise RetrievalError('Temporary server error during login')
122
122
 
123
123
  if 'Location' not in response.headers:
124
+ if 'consent' in url:
125
+ raise AuthenticationError('Could not find Location in headers, probably due to missing consent. Try visiting: ' + url)
124
126
  raise APICompatibilityError('Forwarding without Location in headers')
125
127
 
126
128
  url = response.headers['Location']
@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING
4
4
 
5
5
  import threading
6
6
  import os
7
+ import traceback
7
8
  import logging
8
9
  import netrc
9
10
  from datetime import datetime, timedelta, timezone
@@ -22,7 +23,7 @@ from carconnectivity.doors import Doors
22
23
  from carconnectivity.windows import Windows
23
24
  from carconnectivity.lights import Lights
24
25
  from carconnectivity.drive import GenericDrive, ElectricDrive, CombustionDrive
25
- from carconnectivity.attributes import BooleanAttribute, DurationAttribute, TemperatureAttribute
26
+ from carconnectivity.attributes import GenericAttribute, BooleanAttribute, DurationAttribute, TemperatureAttribute
26
27
  from carconnectivity.charging import Charging
27
28
  from carconnectivity.position import Position
28
29
  from carconnectivity.climatization import Climatization
@@ -72,6 +73,7 @@ class Connector(BaseConnector):
72
73
  """
73
74
  def __init__(self, connector_id: str, car_connectivity: CarConnectivity, config: Dict) -> None:
74
75
  BaseConnector.__init__(self, connector_id=connector_id, car_connectivity=car_connectivity, config=config, log=LOG, api_log=LOG_API)
76
+ self._healthy = False
75
77
 
76
78
  self._mqtt_client: SkodaMQTTClient = SkodaMQTTClient(skoda_connector=self)
77
79
 
@@ -81,6 +83,16 @@ class Connector(BaseConnector):
81
83
 
82
84
  self.connected: BooleanAttribute = BooleanAttribute(name="connected", parent=self, tags={'connector_custom'})
83
85
  self.interval: DurationAttribute = DurationAttribute(name="interval", parent=self, tags={'connector_custom'})
86
+
87
+ def __check_interval(attribute: GenericAttribute, value: Any) -> Any:
88
+ del attribute
89
+ if value is not None and value < timedelta(seconds=180):
90
+ raise ValueError('Intervall must be at least 180 seconds')
91
+ return value
92
+
93
+ self.interval._is_changeable = True # pylint: disable=protected-access
94
+ self.interval._add_on_set_hook(__check_interval) # pylint: disable=protected-access
95
+
84
96
  self.commands: Commands = Commands(parent=self)
85
97
 
86
98
  self.user_id: Optional[str] = None
@@ -154,12 +166,15 @@ class Connector(BaseConnector):
154
166
  self._stop_event.clear()
155
167
  # Start background thread for Rest API polling
156
168
  self._background_thread = threading.Thread(target=self._background_loop, daemon=False)
169
+ self._background_thread.name = 'carconnectivity.connectors.skoda-background'
157
170
  self._background_thread.start()
158
171
  # Start background thread for MQTT connection
159
172
  self._background_connect_thread = threading.Thread(target=self._background_connect_loop, daemon=False)
173
+ self._background_connect_thread.name = 'carconnectivity.connectors.skoda-background_connect'
160
174
  self._background_connect_thread.start()
161
175
  # Start MQTT thread
162
176
  self._mqtt_client.loop_start()
177
+ self._healthy = True
163
178
 
164
179
  def _background_connect_loop(self) -> None:
165
180
  while not self._stop_event.is_set():
@@ -204,6 +219,10 @@ class Connector(BaseConnector):
204
219
  except TemporaryAuthenticationError as err:
205
220
  LOG.error('Temporary authentification error during update (%s). Will try again after configured interval of %ss', str(err), interval)
206
221
  self._stop_event.wait(interval)
222
+ except Exception as err:
223
+ LOG.critical('Critical error during update: %s', traceback.format_exc())
224
+ self._healthy = False
225
+ raise err
207
226
  else:
208
227
  self._stop_event.wait(interval)
209
228
 
@@ -236,12 +255,12 @@ class Connector(BaseConnector):
236
255
  self.car_connectivity.garage.remove_vehicle(vehicle.id)
237
256
  vehicle.enabled = False
238
257
  self._stop_event.set()
258
+ self.session.close()
239
259
  if self._background_thread is not None:
240
260
  self._background_thread.join()
241
261
  if self._background_connect_thread is not None:
242
262
  self._background_connect_thread.join()
243
263
  self.persist()
244
- self.session.close()
245
264
  return super().shutdown()
246
265
 
247
266
  def fetch_all(self) -> None:
@@ -339,6 +358,8 @@ class Connector(BaseConnector):
339
358
  vehicle_to_update = self.fetch_vehicle_status(vehicle_to_update)
340
359
  vehicle_to_update = self.fetch_driving_range(vehicle_to_update)
341
360
  if vehicle_to_update.capabilities is not None and vehicle_to_update.capabilities.enabled:
361
+ if vehicle_to_update.capabilities.has_capability('READINESS'):
362
+ vehicle_to_update = self.fetch_connection_status(vehicle_to_update)
342
363
  if vehicle_to_update.capabilities.has_capability('PARKING_POSITION'):
343
364
  vehicle_to_update = self.fetch_position(vehicle_to_update)
344
365
  if vehicle_to_update.capabilities.has_capability('CHARGING') and isinstance(vehicle_to_update, SkodaElectricVehicle):
@@ -347,6 +368,27 @@ class Connector(BaseConnector):
347
368
  vehicle_to_update = self.fetch_air_conditioning(vehicle_to_update)
348
369
  if vehicle_to_update.capabilities.has_capability('VEHICLE_HEALTH_INSPECTION'):
349
370
  vehicle_to_update = self.fetch_maintenance(vehicle_to_update)
371
+ vehicle_to_update = self.decide_state(vehicle_to_update)
372
+
373
+ def decide_state(self, vehicle: SkodaVehicle) -> SkodaVehicle:
374
+ """
375
+ Decides the state of the vehicle based on the current data.
376
+
377
+ Args:
378
+ vehicle (SkodaVehicle): The Skoda vehicle object.
379
+
380
+ Returns:
381
+ SkodaVehicle: The Skoda vehicle object with the updated state.
382
+ """
383
+ if vehicle is not None:
384
+ if vehicle.in_motion is not None and vehicle.in_motion.enabled and vehicle.in_motion.value:
385
+ vehicle.state._set_value(GenericVehicle.State.IGNITION_ON) # pylint: disable=protected-access
386
+ elif vehicle.position is not None and vehicle.position.enabled and vehicle.position.position_type is not None \
387
+ and vehicle.position.position_type.enabled and vehicle.position.position_type.value == Position.PositionType.PARKING:
388
+ vehicle.state._set_value(GenericVehicle.State.PARKED) # pylint: disable=protected-access
389
+ else:
390
+ vehicle.state._set_value(None) # pylint: disable=protected-access
391
+ return vehicle
350
392
 
351
393
  def fetch_charging(self, vehicle: SkodaElectricVehicle, no_cache: bool = False) -> SkodaElectricVehicle:
352
394
  """
@@ -555,6 +597,41 @@ class Connector(BaseConnector):
555
597
  vehicle.charging.errors.clear()
556
598
  log_extra_keys(LOG_API, 'charging data', data, {'carCapturedTimestamp', 'status', 'isVehicleInSavedLocation', 'errors', 'settings'})
557
599
  return vehicle
600
+
601
+ def fetch_connection_status(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
602
+ """
603
+ Fetches the connection status of the given Skoda vehicle and updates its connection attributes.
604
+
605
+ Args:
606
+ vehicle (SkodaVehicle): The Skoda vehicle object containing the VIN and connection attributes.
607
+
608
+ Returns:
609
+ SkodaVehicle: The updated Skoda vehicle object with the fetched connection data.
610
+
611
+ Raises:
612
+ APIError: If the VIN is missing.
613
+ ValueError: If the vehicle has no connection object.
614
+ """
615
+ vin = vehicle.vin.value
616
+ if vin is None:
617
+ raise APIError('VIN is missing')
618
+ url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/connection-status/{vin}/readiness'
619
+ data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
620
+ # {'unreachable': False, 'inMotion': False, 'batteryProtectionLimitOn': False}
621
+ if data is not None:
622
+ if 'unreachable' in data and data['unreachable'] is not None:
623
+ if data['unreachable']:
624
+ vehicle.connection_state._set_value(vehicle.ConnectionState.OFFLINE) # pylint: disable=protected-access
625
+ else:
626
+ vehicle.connection_state._set_value(vehicle.ConnectionState.REACHABLE) # pylint: disable=protected-access
627
+ else:
628
+ vehicle.connection_state._set_value(None) # pylint: disable=protected-access
629
+ if 'inMotion' in data and data['inMotion'] is not None:
630
+ vehicle.in_motion._set_value(data['inMotion']) # pylint: disable=protected-access
631
+ else:
632
+ vehicle.in_motion._set_value(None) # pylint: disable=protected-access
633
+ log_extra_keys(LOG_API, 'connection status', data, {'unreachable'})
634
+ return vehicle
558
635
 
559
636
  def fetch_position(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
560
637
  """
@@ -716,12 +793,19 @@ class Connector(BaseConnector):
716
793
  # pylint: disable-next=protected-access
717
794
  vehicle.climatization.settings.target_temperature._add_on_set_hook(self.__on_air_conditioning_target_temperature_change)
718
795
  vehicle.climatization.settings.target_temperature._is_changeable = True # pylint: disable=protected-access
796
+ precision: float = 0.5
797
+ min_temperature: Optional[float] = None
798
+ max_temperature: Optional[float] = None
719
799
  unit: Temperature = Temperature.UNKNOWN
720
800
  if 'unitInCar' in data['targetTemperature'] and data['targetTemperature']['unitInCar'] is not None:
721
801
  if data['targetTemperature']['unitInCar'] == 'CELSIUS':
722
802
  unit = Temperature.C
803
+ min_temperature: Optional[float] = 16
804
+ max_temperature: Optional[float] = 29.5
723
805
  elif data['targetTemperature']['unitInCar'] == 'FAHRENHEIT':
724
806
  unit = Temperature.F
807
+ min_temperature: Optional[float] = 61
808
+ max_temperature: Optional[float] = 85
725
809
  elif data['targetTemperature']['unitInCar'] == 'KELVIN':
726
810
  unit = Temperature.K
727
811
  else:
@@ -731,6 +815,10 @@ class Connector(BaseConnector):
731
815
  vehicle.climatization.settings.target_temperature._set_value(value=data['targetTemperature']['temperatureValue'],
732
816
  measured=captured_at,
733
817
  unit=unit)
818
+ vehicle.climatization.settings.target_temperature.precision = precision
819
+ vehicle.climatization.settings.target_temperature.minimum = min_temperature
820
+ vehicle.climatization.settings.target_temperature.maximum = max_temperature
821
+
734
822
  else:
735
823
  # pylint: disable-next=protected-access
736
824
  vehicle.climatization.settings.target_temperature._set_value(value=None, measured=captured_at, unit=unit)
@@ -1428,9 +1516,10 @@ class Connector(BaseConnector):
1428
1516
  if command_arguments['command'] == ClimatizationStartStopCommand.Command.START:
1429
1517
  command_dict['heaterSource'] = 'ELECTRIC'
1430
1518
  command_dict['targetTemperature'] = {}
1519
+ precision: float = 0.5
1431
1520
  if 'target_temperature' in command_arguments:
1432
1521
  # Round target temperature to nearest 0.5
1433
- command_dict['targetTemperature']['temperatureValue'] = round(command_arguments['target_temperature'] * 2) / 2
1522
+ command_dict['targetTemperature']['temperatureValue'] = round(command_arguments['target_temperature'] / precision) * precision
1434
1523
  if 'target_temperature_unit' in command_arguments:
1435
1524
  if not isinstance(command_arguments['target_temperature_unit'], Temperature):
1436
1525
  raise CommandError('Temperature unit is not of type Temperature')
@@ -1448,8 +1537,10 @@ class Connector(BaseConnector):
1448
1537
  and isinstance(climatization, Climatization) and climatization.settings is not None \
1449
1538
  and climatization.settings.target_temperature is not None and climatization.settings.target_temperature.enabled \
1450
1539
  and climatization.settings.target_temperature.value is not None: # pylint: disable=too-many-boolean-expressions
1540
+ if climatization.settings.target_temperature.precision is not None:
1541
+ precision = climatization.settings.target_temperature.precision
1451
1542
  # Round target temperature to nearest 0.5
1452
- command_dict['targetTemperature']['temperatureValue'] = round(climatization.settings.target_temperature.value * 2) / 2
1543
+ command_dict['targetTemperature']['temperatureValue'] = round(climatization.settings.target_temperature.value / precision) * precision
1453
1544
  if climatization.settings.target_temperature.unit == Temperature.C:
1454
1545
  command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
1455
1546
  elif climatization.settings.target_temperature.unit == Temperature.F:
@@ -1681,3 +1772,6 @@ class Connector(BaseConnector):
1681
1772
  except requests.exceptions.RetryError as retry_error:
1682
1773
  raise CommandError(f'Retrying failed: {retry_error}') from retry_error
1683
1774
  return command_arguments
1775
+
1776
+ def is_healthy(self) -> bool:
1777
+ return self._healthy and super().is_healthy()
@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING
4
4
 
5
5
  from carconnectivity.vehicle import GenericVehicle, ElectricVehicle, CombustionVehicle, HybridVehicle
6
6
  from carconnectivity.charging import Charging
7
+ from carconnectivity.attributes import BooleanAttribute
7
8
 
8
9
  from carconnectivity_connectors.skoda.capability import Capabilities
9
10
  from carconnectivity_connectors.skoda.charging import SkodaCharging
@@ -32,6 +33,8 @@ class SkodaVehicle(GenericVehicle): # pylint: disable=too-many-instance-attribu
32
33
  super().__init__(origin=origin)
33
34
  self.capabilities: Capabilities = origin.capabilities
34
35
  self.capabilities.parent = self
36
+ self.in_motion: BooleanAttribute = origin.in_motion
37
+ self.in_motion.parent = self
35
38
  if SUPPORT_IMAGES:
36
39
  self._car_images = origin._car_images
37
40
 
@@ -39,6 +42,7 @@ class SkodaVehicle(GenericVehicle): # pylint: disable=too-many-instance-attribu
39
42
  super().__init__(vin=vin, garage=garage, managing_connector=managing_connector)
40
43
  self.climatization = SkodaClimatization(vehicle=self, origin=self.climatization)
41
44
  self.capabilities = Capabilities(vehicle=self)
45
+ self.in_motion = BooleanAttribute(name='in_motion', parent=self, tags={'connector_custom'})
42
46
  if SUPPORT_IMAGES:
43
47
  self._car_images: Dict[str, Image.Image] = {}
44
48
  self.manufacturer._set_value(value='Škoda') # pylint: disable=protected-access