pycupra 0.0.6__py3-none-any.whl → 0.0.8__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.
pycupra/__version__.py CHANGED
@@ -3,4 +3,4 @@ pycupra - A Python 3 library for interacting with the My Cupra/My Seat portal.
3
3
 
4
4
  For more details and documentation, visit the github page at https://github.com/WulfgarW/pycupra
5
5
  """
6
- __version__ = "0.0.6"
6
+ __version__ = "0.0.8"
pycupra/connection.py CHANGED
@@ -136,6 +136,10 @@ class Connection:
136
136
  _LOGGER.info(f'Init PyCupra library, version {lib_version}')
137
137
  _LOGGER.debug(f'Using service {self._session_base}')
138
138
 
139
+ self._sessionRequestCounter = 0
140
+ self._sessionRequestTimestamp = datetime.now(tz= None)
141
+ self._sessionRequestCounterHistory = {}
142
+
139
143
 
140
144
  def _clear_cookies(self):
141
145
  self._session._cookie_jar._cookies.clear()
@@ -604,6 +608,19 @@ class Connection:
604
608
  """Perform a HTTP query"""
605
609
  if self._session_fulldebug:
606
610
  _LOGGER.debug(f'HTTP {method} "{url}"')
611
+ try:
612
+ if datetime.now(tz=None).date() != self._sessionRequestTimestamp.date():
613
+ # A new day has begun. Store _sessionRequestCounter in history and reset timestamp and counter
614
+ self._sessionRequestCounterHistory[self._sessionRequestTimestamp.strftime('%Y-%m-%d')]=self._sessionRequestCounter
615
+ _LOGGER.info(f'History of the number of API calls:')
616
+ for key, value in self._sessionRequestCounterHistory.items:
617
+ _LOGGER.info(f' Date: {key}: {value} API calls')
618
+
619
+ self._sessionRequestTimestamp= datetime.now(tz=None)
620
+ self._sessionRequestCounter = 0
621
+ except Exception as e:
622
+ _LOGGER.error(f'Error while preparing output of API call history. Error: {e}')
623
+ self._sessionRequestCounter = self._sessionRequestCounter + 1
607
624
  async with self._session.request(
608
625
  method,
609
626
  url,
@@ -694,7 +711,7 @@ class Connection:
694
711
  for vehicle in self.vehicles:
695
712
  if vehicle.vin not in update_list:
696
713
  _LOGGER.debug(f'Adding {vehicle.vin} for data refresh')
697
- update_list.append(vehicle.update())
714
+ update_list.append(vehicle.update(updateType=1))
698
715
  else:
699
716
  _LOGGER.debug(f'VIN {vehicle.vin} is already queued for data refresh')
700
717
 
@@ -884,6 +901,14 @@ class Connection:
884
901
  _LOGGER.info('Unhandled error while trying to fetch mycar data')
885
902
  except Exception as error:
886
903
  _LOGGER.warning(f'Could not fetch mycar report, error: {error}')
904
+ if data=={}:
905
+ return False
906
+ return data
907
+
908
+ async def getMileage(self, vin, baseurl):
909
+ """Get car information from customer profile, VIN, nickname, etc."""
910
+ await self.set_token(self._session_auth_brand)
911
+ data={}
887
912
  try:
888
913
  response = await self.get(eval(f"f'{API_MILEAGE}'"))
889
914
  if response.get('mileageKm', {}):
pycupra/dashboard.py CHANGED
@@ -444,9 +444,9 @@ class RequestFlash(Switch):
444
444
  return dict(last_result = self.vehicle.honkandflash_action_status)
445
445
 
446
446
 
447
- class RequestUpdate(Switch):
447
+ class RequestRefresh(Switch):
448
448
  def __init__(self):
449
- super().__init__(attr="refresh_data", name="Force data refresh", icon="mdi:car-connected")
449
+ super().__init__(attr="refresh_data", name="Request wakeup vehicle", icon="mdi:car-connected")
450
450
 
451
451
  @property
452
452
  def state(self):
@@ -454,7 +454,7 @@ class RequestUpdate(Switch):
454
454
 
455
455
  async def turn_on(self):
456
456
  await self.vehicle.set_refresh()
457
- await self.vehicle.update()
457
+ await self.vehicle.update(updateType=1) #full update after set_refresh
458
458
  if self.callback is not None:
459
459
  self.callback()
460
460
 
@@ -470,6 +470,31 @@ class RequestUpdate(Switch):
470
470
  return dict(last_result = self.vehicle.refresh_action_status)
471
471
 
472
472
 
473
+ class RequestUpdate(Switch):
474
+ def __init__(self):
475
+ super().__init__(attr="update_data", name="Request full update", icon="mdi:timer-refresh")
476
+
477
+ @property
478
+ def state(self):
479
+ return False #self.vehicle.update
480
+
481
+ async def turn_on(self):
482
+ await self.vehicle.update(updateType=1) #full update after set_refresh
483
+ if self.callback is not None:
484
+ self.callback()
485
+
486
+ async def turn_off(self):
487
+ pass
488
+
489
+ @property
490
+ def assumed_state(self):
491
+ return False
492
+
493
+ #@property
494
+ #def attributes(self):
495
+ # return dict()
496
+
497
+
473
498
  class ElectricClimatisation(Switch):
474
499
  def __init__(self):
475
500
  super().__init__(attr="electric_climatisation", name="Electric Climatisation", icon="mdi:radiator")
@@ -916,6 +941,7 @@ def create_instruments():
916
941
  TrunkLock(),
917
942
  RequestFlash(),
918
943
  RequestHonkAndFlash(),
944
+ RequestRefresh(),
919
945
  RequestUpdate(),
920
946
  WindowHeater(),
921
947
  BatteryClimatisation(),
@@ -1000,6 +1026,12 @@ def create_instruments():
1000
1026
  icon="mdi:clock",
1001
1027
  device_class="timestamp"
1002
1028
  ),
1029
+ Sensor(
1030
+ attr="last_full_update",
1031
+ name="Last full update",
1032
+ icon="mdi:clock",
1033
+ device_class="timestamp"
1034
+ ),
1003
1035
  Sensor(
1004
1036
  attr="parking_time",
1005
1037
  name="Parking time",
pycupra/vehicle.py CHANGED
@@ -74,14 +74,17 @@ class Vehicle:
74
74
  'transactionHistoryHonkFlash': {'active': False, 'reason': 'not supported'},
75
75
  }
76
76
 
77
+ self._last_full_update = datetime.now(tz=None) - timedelta(seconds=1200)
78
+
79
+
77
80
  #### API get and set functions ####
78
81
  # Init and update vehicle data
79
82
  async def discover(self):
80
83
  """Discover vehicle and initial data."""
81
- await asyncio.gather(
82
- self.get_basiccardata(),
83
- return_exceptions=True
84
- )
84
+ #await asyncio.gather(
85
+ # self.get_basiccardata(),
86
+ # return_exceptions=True
87
+ #)
85
88
  # Extract information of relevant capabilities
86
89
  for capa in self._capabilities:
87
90
  id=capa.get('id', '')
@@ -104,13 +107,13 @@ class Vehicle:
104
107
  self._relevantCapabilties[id].update(data)
105
108
 
106
109
 
107
- await self.get_trip_statistic(),
110
+ await self.get_trip_statistic()
108
111
  # Get URLs for model image
109
- self._modelimages = await self.get_modelimageurl(),
112
+ self._modelimages = await self.get_modelimageurl()
110
113
 
111
114
  self._discovered = datetime.now()
112
115
 
113
- async def update(self):
116
+ async def update(self, updateType=0):
114
117
  """Try to fetch data for all known API endpoints."""
115
118
  # Update vehicle information if not discovered or stale information
116
119
  if not self._discovered:
@@ -125,6 +128,21 @@ class Vehicle:
125
128
  # Fetch all data if car is not deactivated
126
129
  if not self.deactivated:
127
130
  try:
131
+ # Data to be updated most often
132
+ await asyncio.gather(
133
+ self.get_charger(),
134
+ self.get_basiccardata(),
135
+ return_exceptions=True
136
+ )
137
+
138
+ fullUpdateExpired = datetime.now(tz=None) - timedelta(seconds= 1100)
139
+ if hasattr(self, '_last_full_update'):
140
+ _LOGGER.debug(f'last_full_update= {self._last_full_update}, fullUpdateExpired= {fullUpdateExpired}.')
141
+ if updateType!=1 and (hasattr(self, '_last_full_update') and self._last_full_update>fullUpdateExpired):
142
+ _LOGGER.debug(f'Just performed small update for vehicle with VIN {self.vin}.')
143
+ return True
144
+
145
+ # Data to be updated less often
128
146
  await asyncio.gather(
129
147
  self.get_preheater(),
130
148
  self.get_climater(),
@@ -132,13 +150,15 @@ class Vehicle:
132
150
  self.get_position(),
133
151
  self.get_statusreport(),
134
152
  self.get_vehicleHealthWarnings(),
135
- self.get_charger(),
136
153
  self.get_departure_timers(),
137
154
  self.get_departure_profiles(),
138
- self.get_basiccardata(),
155
+ self.get_mileage(),
139
156
  #self.get_modelimageurl(), #commented out, because getting the images discover() should be sufficient
140
157
  return_exceptions=True
141
158
  )
159
+ self._last_full_update = datetime.now(tz=None)
160
+ _LOGGER.debug(f'Performed full update for vehicle with VIN {self.vin}.')
161
+ _LOGGER.debug(f'So far about {self._connection._sessionRequestCounter} API calls since {self._connection._sessionRequestTimestamp}.')
142
162
  except:
143
163
  raise SeatException("Update failed")
144
164
  return True
@@ -158,6 +178,12 @@ class Vehicle:
158
178
  if data:
159
179
  self._states.update(data)
160
180
 
181
+ async def get_mileage(self):
182
+ """Fetch basic car data."""
183
+ data = await self._connection.getMileage(self.vin, self._apibase)
184
+ if data:
185
+ self._states.update(data)
186
+
161
187
  async def get_preheater(self):
162
188
  """Fetch pre-heater data if function is enabled."""
163
189
  _LOGGER.info('get_preheater() not implemented yet')
@@ -320,7 +346,7 @@ class Vehicle:
320
346
  raise SeatInvalidRequestException('Remote start/stop of charger is not supported.')
321
347
  if self._requests['batterycharge'].get('id', False):
322
348
  timestamp = self._requests.get('batterycharge', {}).get('timestamp', datetime.now())
323
- expired = datetime.now() - timedelta(minutes=3)
349
+ expired = datetime.now() - timedelta(minutes=1)
324
350
  if expired > timestamp:
325
351
  self._requests.get('batterycharge', {}).pop('id')
326
352
  else:
@@ -574,7 +600,7 @@ class Vehicle:
574
600
  raise SeatInvalidRequestException('Departure timers are not supported.')
575
601
  if self._requests['departuretimer'].get('id', False):
576
602
  timestamp = self._requests.get('departuretimer', {}).get('timestamp', datetime.now())
577
- expired = datetime.now() - timedelta(minutes=3)
603
+ expired = datetime.now() - timedelta(minutes=1)
578
604
  if expired > timestamp:
579
605
  self._requests.get('departuretimer', {}).pop('id')
580
606
  else:
@@ -848,7 +874,7 @@ class Vehicle:
848
874
  raise SeatInvalidRequestException('Remote control of climatisation functions is not supported.')
849
875
  if self._requests['climatisation'].get('id', False):
850
876
  timestamp = self._requests.get('climatisation', {}).get('timestamp', datetime.now())
851
- expired = datetime.now() - timedelta(minutes=3)
877
+ expired = datetime.now() - timedelta(minutes=1)
852
878
  if expired > timestamp:
853
879
  self._requests.get('climatisation', {}).pop('id')
854
880
  else:
@@ -913,7 +939,7 @@ class Vehicle:
913
939
  raise SeatInvalidRequestException('No parking heater support.')
914
940
  if self._requests['preheater'].get('id', False):
915
941
  timestamp = self._requests.get('preheater', {}).get('timestamp', datetime.now())
916
- expired = datetime.now() - timedelta(minutes=3)
942
+ expired = datetime.now() - timedelta(minutes=1)
917
943
  if expired > timestamp:
918
944
  self._requests.get('preheater', {}).pop('id')
919
945
  else:
@@ -1053,7 +1079,7 @@ class Vehicle:
1053
1079
  raise SeatInvalidRequestException('Data refresh is not supported.')
1054
1080
  if self._requests['refresh'].get('id', False):
1055
1081
  timestamp = self._requests.get('refresh', {}).get('timestamp', datetime.now() - timedelta(minutes=5))
1056
- expired = datetime.now() - timedelta(minutes=3)
1082
+ expired = datetime.now() - timedelta(minutes=1)
1057
1083
  if expired > timestamp:
1058
1084
  self._requests.get('refresh', {}).pop('id')
1059
1085
  else:
@@ -1228,6 +1254,18 @@ class Vehicle:
1228
1254
  if 'updatedAt' in self.attrs.get('status', {}):
1229
1255
  return True
1230
1256
 
1257
+ # Update status
1258
+ @property
1259
+ def last_full_update(self):
1260
+ """Return when the last full update for the vehicle took place."""
1261
+ return self._last_full_update.astimezone(tz=None)
1262
+
1263
+ @property
1264
+ def is_last_full_update_supported(self):
1265
+ """Return when last full update for vehicle took place."""
1266
+ if hasattr(self,'_last_full_update'):
1267
+ return True
1268
+
1231
1269
  # Service information
1232
1270
  @property
1233
1271
  def distance(self):
@@ -2683,6 +2721,16 @@ class Vehicle:
2683
2721
  if self._connectivities.get('mode', '') == 'online':
2684
2722
  return True
2685
2723
 
2724
+ @property
2725
+ def update_data(self):
2726
+ """Get state of data update"""
2727
+ return False
2728
+
2729
+ @property
2730
+ def is_update_data_supported(self):
2731
+ """Data update is supported."""
2732
+ return True
2733
+
2686
2734
  # Honk and flash
2687
2735
  @property
2688
2736
  def request_honkandflash(self):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycupra
3
- Version: 0.0.6
3
+ Version: 0.0.8
4
4
  Summary: A library to read and send vehicle data via Cupra/Seat portal using the same API calls as the MyCupra/MySeat mobile app.
5
5
  Home-page: https://github.com/WulfgarW/pycupra
6
6
  Author: WulfgarW
@@ -0,0 +1,13 @@
1
+ pycupra/__init__.py,sha256=VPzUfKd5mBFD1UERNV61FbGHih5dQPupLgIfYtmIUi4,230
2
+ pycupra/__version__.py,sha256=g845Cq_z0hjlwG9RG4nM9vit4t_cEbb5HkAMZ7Iav3Y,207
3
+ pycupra/connection.py,sha256=fNjyrj6nBLOfEpKcygDs47UFlWjmR4tgJa1KPLLNZLU,82411
4
+ pycupra/const.py,sha256=VEYH8TUsJGJwBwloaajwoElYd0qxE7oesvoagvDdE-4,10161
5
+ pycupra/dashboard.py,sha256=Dqs4qDF-rEoYXoS7NrNsvRMsCGzoAjVUEbfTSOdDAXo,41310
6
+ pycupra/exceptions.py,sha256=Nq_F79GP8wjHf5lpvPy9TbSIrRHAJrFMo0T1N9TcgSQ,2917
7
+ pycupra/utilities.py,sha256=cH4MiIzT2WlHgmnl_E7rR0R5LvCXfDNvirJolct50V8,2563
8
+ pycupra/vehicle.py,sha256=J5jUAseVTtb9HK0-RvCi-VzkJ1z0h4Phn_Tgno6Sgzk,122420
9
+ pycupra-0.0.8.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
10
+ pycupra-0.0.8.dist-info/METADATA,sha256=Z8QMsbcu3IXFWgeNLKTk91sGirawTdhXhEoCqFvhAPs,2578
11
+ pycupra-0.0.8.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
12
+ pycupra-0.0.8.dist-info/top_level.txt,sha256=9Lbj_jG4JvpGwt6K3AwhWFc0XieDnuHFOP4x44wSXSQ,8
13
+ pycupra-0.0.8.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- pycupra/__init__.py,sha256=VPzUfKd5mBFD1UERNV61FbGHih5dQPupLgIfYtmIUi4,230
2
- pycupra/__version__.py,sha256=YbGN0knQ3fpcD8GGO6OA7ZBYa-fQ4unLaw-NTYREVn8,207
3
- pycupra/connection.py,sha256=4bcaHn6AwAAY9e3MfVwlPMQzKJZQo_BZttts0lubnro,81120
4
- pycupra/const.py,sha256=VEYH8TUsJGJwBwloaajwoElYd0qxE7oesvoagvDdE-4,10161
5
- pycupra/dashboard.py,sha256=difLM3R2uXUrVslUjqzH8nN6WPJA-u26rHlGUU6W8Oo,40454
6
- pycupra/exceptions.py,sha256=Nq_F79GP8wjHf5lpvPy9TbSIrRHAJrFMo0T1N9TcgSQ,2917
7
- pycupra/utilities.py,sha256=cH4MiIzT2WlHgmnl_E7rR0R5LvCXfDNvirJolct50V8,2563
8
- pycupra/vehicle.py,sha256=vlSuvrspX_I1RlS8KoZkN09u5cCNNdcIslsA0xPUuqA,120442
9
- pycupra-0.0.6.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
10
- pycupra-0.0.6.dist-info/METADATA,sha256=k-diSYUQOySb2FxopmNeWMOXqf_FQG1A5yGwnMpo03M,2578
11
- pycupra-0.0.6.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
12
- pycupra-0.0.6.dist-info/top_level.txt,sha256=9Lbj_jG4JvpGwt6K3AwhWFc0XieDnuHFOP4x44wSXSQ,8
13
- pycupra-0.0.6.dist-info/RECORD,,