carconnectivity-connector-skoda 0.1a10__py3-none-any.whl → 0.1a11__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: carconnectivity-connector-skoda
3
- Version: 0.1a10
3
+ Version: 0.1a11
4
4
  Summary: CarConnectivity connector for Skoda services
5
5
  Author: Till Steinbach
6
6
  License: MIT License
@@ -1,9 +1,9 @@
1
1
  carconnectivity_connectors/skoda/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- carconnectivity_connectors/skoda/_version.py,sha256=-6RZUfHP4tKhu5IUXqJlxl_E1uda42gm5BR0t7cpGAg,409
2
+ carconnectivity_connectors/skoda/_version.py,sha256=lXj09wNlQE2ygonVVc_Fk2FN_1_4nx6sHnElAwbvzHM,409
3
3
  carconnectivity_connectors/skoda/capability.py,sha256=JlNEaisVYF8qWv0wNDHTaas36uIpTIQ3NVR69wesiYQ,4513
4
4
  carconnectivity_connectors/skoda/charging.py,sha256=oDHxZxrfTMvtYCJxmGfKFeWVMH4ceQ5HTKRAspnsunU,3312
5
- carconnectivity_connectors/skoda/connector.py,sha256=c0vi2H-yoJM6uX7PMIWLzJ-YeksyXyqBMpJT21uyc_I,61826
6
- carconnectivity_connectors/skoda/mqtt_client.py,sha256=ZESgR4JRPPgAiLLEPvcnD6UeSaL-dMC1MvOWaqDdvj8,27944
5
+ carconnectivity_connectors/skoda/connector.py,sha256=xiPPT6XTVWsLvu0iqZvSPpgtHm3DrR1TKUL4d4NRz2Y,62158
6
+ carconnectivity_connectors/skoda/mqtt_client.py,sha256=ZB56Za1k5vbxQPl5qCvukE46Tq1O0-Cn19kBz_bDJZM,31782
7
7
  carconnectivity_connectors/skoda/vehicle.py,sha256=H3GRDNimMghFwFi--y9BsgoSK3pMibNf_l6SsDN6gvQ,2759
8
8
  carconnectivity_connectors/skoda/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  carconnectivity_connectors/skoda/auth/auth_util.py,sha256=dGLUbUre0HBsTg_Ii5vW34f8DLrCykYJYCyzEvUBBEE,4434
@@ -12,8 +12,8 @@ carconnectivity_connectors/skoda/auth/openid_session.py,sha256=LusWi2FZZIL3buodG
12
12
  carconnectivity_connectors/skoda/auth/session_manager.py,sha256=Uf1vujuDBYUCAXhYToOsZkgbTtfmY3Qe0ICTfwomBpI,2899
13
13
  carconnectivity_connectors/skoda/auth/skoda_web_session.py,sha256=cjzMkzx473Sh-4RgZAQULeRRcxB1MboddldCVM_y5LE,10640
14
14
  carconnectivity_connectors/skoda/auth/helpers/blacklist_retry.py,sha256=f3wsiY5bpHDBxp7Va1Mv9nKJ4u3qnCHZZmDu78_AhMk,1251
15
- carconnectivity_connector_skoda-0.1a10.dist-info/LICENSE,sha256=PIwI1alwDyOfvEQHdGCm2u9uf_mGE8030xZDfun0xTo,1071
16
- carconnectivity_connector_skoda-0.1a10.dist-info/METADATA,sha256=_xdTO18nMfBi4hYnpdo6Cr3kCHegfhu9IJ1_EJIJOBo,5327
17
- carconnectivity_connector_skoda-0.1a10.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
18
- carconnectivity_connector_skoda-0.1a10.dist-info/top_level.txt,sha256=KqA8GviZsDH4PtmnwSQsz0HB_w-TWkeEHLIRNo5dTaI,27
19
- carconnectivity_connector_skoda-0.1a10.dist-info/RECORD,,
15
+ carconnectivity_connector_skoda-0.1a11.dist-info/LICENSE,sha256=PIwI1alwDyOfvEQHdGCm2u9uf_mGE8030xZDfun0xTo,1071
16
+ carconnectivity_connector_skoda-0.1a11.dist-info/METADATA,sha256=tS2ccqTBaAHPxFbNRA7wlbJQPiqLDU8J9h62FQcXvb8,5327
17
+ carconnectivity_connector_skoda-0.1a11.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
18
+ carconnectivity_connector_skoda-0.1a11.dist-info/top_level.txt,sha256=KqA8GviZsDH4PtmnwSQsz0HB_w-TWkeEHLIRNo5dTaI,27
19
+ carconnectivity_connector_skoda-0.1a11.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.1a10'
15
+ __version__ = version = '0.1a11'
16
16
  __version_tuple__ = version_tuple = (0, 1)
@@ -312,7 +312,7 @@ class Connector(BaseConnector):
312
312
  if vehicle_to_update.capabilities.has_capability('AIR_CONDITIONING'):
313
313
  vehicle_to_update = self.fetch_air_conditioning(vehicle_to_update)
314
314
 
315
- def fetch_charging(self, vehicle: SkodaElectricVehicle) -> SkodaElectricVehicle:
315
+ def fetch_charging(self, vehicle: SkodaElectricVehicle, no_cache: bool = False) -> SkodaElectricVehicle:
316
316
  """
317
317
  Fetches the charging information for a given Skoda electric vehicle.
318
318
 
@@ -329,7 +329,7 @@ class Connector(BaseConnector):
329
329
  if vehicle.charging is None:
330
330
  raise ValueError('Vehicle has no charging object')
331
331
  url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}'
332
- data: Dict[str, Any] | None = self._fetch_data(url, session=self.session)
332
+ data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
333
333
  if data is not None:
334
334
  if 'carCapturedTimestamp' in data and data['carCapturedTimestamp'] is not None:
335
335
  captured_at: datetime = robust_time_parse(data['carCapturedTimestamp'])
@@ -372,7 +372,7 @@ class Connector(BaseConnector):
372
372
  log_extra_keys(LOG_API, 'charging data', data, {'carCapturedTimestamp', 'status'})
373
373
  return vehicle
374
374
 
375
- def fetch_position(self, vehicle: SkodaVehicle) -> SkodaVehicle:
375
+ def fetch_position(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
376
376
  """
377
377
  Fetches the position of the given Skoda vehicle and updates its position attributes.
378
378
 
@@ -392,7 +392,7 @@ class Connector(BaseConnector):
392
392
  if vehicle.position is None:
393
393
  raise ValueError('Vehicle has no charging object')
394
394
  url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/maps/positions?vin={vin}'
395
- data: Dict[str, Any] | None = self._fetch_data(url, session=self.session)
395
+ data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
396
396
  if data is not None:
397
397
  if 'positions' in data and data['positions'] is not None:
398
398
  for position_dict in data['positions']:
@@ -426,7 +426,7 @@ class Connector(BaseConnector):
426
426
  vehicle.position.position_type._set_value(None) # pylint: disable=protected-access
427
427
  return vehicle
428
428
 
429
- def fetch_air_conditioning(self, vehicle: SkodaVehicle) -> SkodaVehicle:
429
+ def fetch_air_conditioning(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
430
430
  """
431
431
  Fetches the air conditioning data for a given Skoda vehicle and updates the vehicle object with the retrieved data.
432
432
 
@@ -451,7 +451,7 @@ class Connector(BaseConnector):
451
451
  if vehicle.position is None:
452
452
  raise ValueError('Vehicle has no charging object')
453
453
  url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}'
454
- data: Dict[str, Any] | None = self._fetch_data(url, session=self.session)
454
+ data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
455
455
  if data is not None:
456
456
  if 'carCapturedTimestamp' in data and data['carCapturedTimestamp'] is not None:
457
457
  captured_at: datetime = robust_time_parse(data['carCapturedTimestamp'])
@@ -529,7 +529,7 @@ class Connector(BaseConnector):
529
529
  'targetTemperature', 'outsideTemperature'})
530
530
  return vehicle
531
531
 
532
- def fetch_vehicle_details(self, vehicle: SkodaVehicle) -> SkodaVehicle:
532
+ def fetch_vehicle_details(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
533
533
  """
534
534
  Fetches the details of a vehicle from the Skoda API.
535
535
 
@@ -544,7 +544,7 @@ class Connector(BaseConnector):
544
544
  raise APIError('VIN is missing')
545
545
  url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/garage/vehicles/{vin}?' \
546
546
  'connectivityGenerations=MOD1&connectivityGenerations=MOD2&connectivityGenerations=MOD3&connectivityGenerations=MOD4'
547
- vehicle_data: Dict[str, Any] | None = self._fetch_data(url, self.session)
547
+ vehicle_data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
548
548
  if vehicle_data:
549
549
  if 'softwareVersion' in vehicle_data and vehicle_data['softwareVersion'] is not None:
550
550
  vehicle.software.version._set_value(vehicle_data['softwareVersion']) # pylint: disable=protected-access
@@ -582,7 +582,7 @@ class Connector(BaseConnector):
582
582
  log_extra_keys(LOG_API, 'api/v2/garage/vehicles/VIN', vehicle_data, {'softwareVersion'})
583
583
  return vehicle
584
584
 
585
- def fetch_driving_range(self, vehicle: SkodaVehicle) -> SkodaVehicle:
585
+ def fetch_driving_range(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
586
586
  """
587
587
  Fetches the driving range data for a given Skoda vehicle and updates the vehicle object accordingly.
588
588
 
@@ -605,7 +605,7 @@ class Connector(BaseConnector):
605
605
  if vin is None:
606
606
  raise APIError('VIN is missing')
607
607
  url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/vehicle-status/{vin}/driving-range'
608
- range_data: Dict[str, Any] | None = self._fetch_data(url, self.session)
608
+ range_data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
609
609
  if range_data:
610
610
  captured_at: datetime = robust_time_parse(range_data['carCapturedTimestamp'])
611
611
  # Check vehicle type and if it does not match the current vehicle type, create a new vehicle object using copy constructor
@@ -692,7 +692,7 @@ class Connector(BaseConnector):
692
692
  'secondaryEngineRange'})
693
693
  return vehicle
694
694
 
695
- def fetch_vehicle_status_second_api(self, vehicle: SkodaVehicle) -> SkodaVehicle:
695
+ def fetch_vehicle_status_second_api(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
696
696
  """
697
697
  Fetches the status of a vehicle from other Skoda API.
698
698
 
@@ -706,7 +706,7 @@ class Connector(BaseConnector):
706
706
  if vin is None:
707
707
  raise APIError('VIN is missing')
708
708
  url = f'https://api.connect.skoda-auto.cz/api/v2/vehicle-status/{vin}'
709
- vehicle_status_data: Dict[str, Any] | None = self._fetch_data(url, self.session)
709
+ vehicle_status_data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
710
710
  if vehicle_status_data:
711
711
  if 'remote' in vehicle_status_data and vehicle_status_data['remote'] is not None:
712
712
  vehicle_status_data = vehicle_status_data['remote']
@@ -881,10 +881,11 @@ class Connector(BaseConnector):
881
881
  """
882
882
  self._elapsed.append(elapsed)
883
883
 
884
- def _fetch_data(self, url, session, force=False, allow_empty=False, allow_http_error=False, allowed_errors=None) -> Optional[Dict[str, Any]]: # noqa: C901
884
+ def _fetch_data(self, url, session, no_cache=False, allow_empty=False, allow_http_error=False,
885
+ allowed_errors=None) -> Optional[Dict[str, Any]]: # noqa: C901
885
886
  data: Optional[Dict[str, Any]] = None
886
887
  cache_date: Optional[datetime] = None
887
- if not force and (self.max_age is not None and session.cache is not None and url in session.cache):
888
+ if not no_cache and (self.max_age is not None and session.cache is not None and url in session.cache):
888
889
  data, cache_date_string = session.cache[url]
889
890
  cache_date = datetime.fromisoformat(cache_date_string)
890
891
  if data is None or self.max_age is None \
@@ -7,6 +7,7 @@ import logging
7
7
  import uuid
8
8
  import ssl
9
9
  import json
10
+ import threading
10
11
  from datetime import timedelta, timezone
11
12
 
12
13
  from paho.mqtt.client import Client
@@ -57,6 +58,8 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
57
58
  self.on_subscribe = self._on_subscribe_callback
58
59
  self.subscribed_topics: Set[str] = set()
59
60
 
61
+ self.delayed_access_function_timers: Dict[str, threading.Timer] = {}
62
+
60
63
  self.tls_set(cert_reqs=ssl.CERT_NONE)
61
64
 
62
65
  def connect(self, *args, **kwargs) -> MQTTErrorCode:
@@ -437,7 +440,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
437
440
  return
438
441
 
439
442
  # service_events
440
- match = re.match(r'^(?P<user_id>[0-9a-fA-F-]+)/(?P<vin>[A-Z0-9]+)/service-event/(?P<service_event>[a-zA-Z0-9-_]+)$', msg.topic)
443
+ match = re.match(r'^(?P<user_id>[0-9a-fA-F-]+)/(?P<vin>[A-Z0-9]+)/service-event/(?P<service_event>[a-zA-Z0-9-_/]+)$', msg.topic)
441
444
  if match:
442
445
  user_id: str = match.group('user_id')
443
446
  vin: str = match.group('vin')
@@ -481,7 +484,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
481
484
  # If charging state changed, fetch charging again
482
485
  if old_charging_state != charging_state:
483
486
  try:
484
- self._skoda_connector.fetch_charging(vehicle)
487
+ self._skoda_connector.fetch_charging(vehicle, no_cache=True)
485
488
  except CarConnectivityError as e:
486
489
  LOG.error('Error while fetching charging: %s', e)
487
490
  if 'timeToFinish' in data['data'] and data['data']['timeToFinish'] is not None \
@@ -507,12 +510,58 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
507
510
  vehicle: Optional[GenericVehicle] = self._skoda_connector.car_connectivity.garage.get_vehicle(vin)
508
511
  if isinstance(vehicle, SkodaVehicle):
509
512
  try:
510
- self._skoda_connector.fetch_air_conditioning(vehicle)
513
+ self._skoda_connector.fetch_air_conditioning(vehicle, no_cache=True)
511
514
  except CarConnectivityError as e:
512
515
  LOG.error('Error while fetching charging: %s', e)
513
516
  LOG_API.info('Received event name %s service event %s for vehicle %s from user %s: %s', data['name'],
514
517
  service_event, vin, user_id, msg.payload)
515
518
  return
519
+ elif service_event == 'vehicle-status/access':
520
+ if 'name' in data and data['name'] == 'change-access':
521
+ if 'data' in data and data['data'] is not None:
522
+ vehicle: Optional[GenericVehicle] = self._skoda_connector.car_connectivity.garage.get_vehicle(vin)
523
+ if isinstance(vehicle, SkodaVehicle):
524
+ def delayed_access_function(vehicle: SkodaVehicle):
525
+ """
526
+ Function to be executed after a delay of two seconds.
527
+ """
528
+ vin = vehicle.id
529
+ self.delayed_access_function_timers.pop(vin)
530
+ if vehicle.capabilities is not None and vehicle.capabilities.enabled \
531
+ and vehicle.capabilities.has_capability('CHARGING') and isinstance(vehicle, SkodaElectricVehicle):
532
+ try:
533
+ self._skoda_connector.fetch_charging(vehicle, no_cache=True)
534
+ except CarConnectivityError as e:
535
+ LOG.error('Error while fetching charging: %s', e)
536
+ if vehicle.capabilities is not None and vehicle.capabilities.enabled \
537
+ and vehicle.capabilities.has_capability('PARKING_POSITION'):
538
+ try:
539
+ self._skoda_connector.fetch_position(vehicle, no_cache=True)
540
+ except CarConnectivityError as e:
541
+ LOG.error('Error while fetching position: %s', e)
542
+ if vehicle.capabilities is not None and vehicle.capabilities.enabled \
543
+ and vehicle.capabilities.has_capability('AIR_CONDITIONING'):
544
+ try:
545
+ self._skoda_connector.fetch_air_conditioning(vehicle, no_cache=True)
546
+ except CarConnectivityError as e:
547
+ LOG.error('Error while fetching air conditioning: %s', e)
548
+ try:
549
+ self._skoda_connector.fetch_vehicle_status_second_api(vehicle, no_cache=True)
550
+ except CarConnectivityError as e:
551
+ LOG.error('Error while fetching status second API: %s', e)
552
+ try:
553
+ self._skoda_connector.fetch_driving_range(vehicle, no_cache=True)
554
+ except CarConnectivityError as e:
555
+ LOG.error('Error while fetching driving range: %s', e)
556
+
557
+ if vin in self.delayed_access_function_timers:
558
+ self.delayed_access_function_timers[vin].cancel()
559
+ self.delayed_access_function_timers[vin] = threading.Timer(2.0, delayed_access_function, kwargs={'vehicle': vehicle})
560
+ self.delayed_access_function_timers[vin].start()
561
+
562
+ LOG_API.info('Received event name %s service event %s for vehicle %s from user %s: %s', data['name'],
563
+ service_event, vin, user_id, msg.payload)
564
+ return
516
565
  LOG_API.info('Received unknown service event %s for vehicle %s from user %s: %s', service_event, vin, user_id, msg.payload)
517
566
  return
518
567
  # service_events
@@ -530,7 +579,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
530
579
  if data['status'] == 'COMPLETED_SUCCESS':
531
580
  LOG.debug('Received %s operation request for vehicle %s from user %s', operation_request, vin, user_id)
532
581
  try:
533
- self._skoda_connector.fetch_air_conditioning(vehicle)
582
+ self._skoda_connector.fetch_air_conditioning(vehicle, no_cache=True)
534
583
  except CarConnectivityError as e:
535
584
  LOG.error('Error while fetching air-conditioning: %s', e)
536
585
  return