pycupra 0.0.12__tar.gz → 0.0.14__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycupra
3
- Version: 0.0.12
3
+ Version: 0.0.14
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
@@ -46,6 +46,7 @@ RESOURCES = [
46
46
  "charge_max_ampere",
47
47
  "charger_action_status",
48
48
  "charging",
49
+ "charge_max_ampere",
49
50
  "charge_rate",
50
51
  "charging_power",
51
52
  "charging_state",
@@ -71,7 +72,7 @@ RESOURCES = [
71
72
  "door_locked",
72
73
  "electric_climatisation",
73
74
  "electric_range",
74
- #"energy_flow",
75
+ "energy_flow",
75
76
  "external_power",
76
77
  "fuel_level",
77
78
  "hood_closed",
@@ -522,7 +523,7 @@ async def main():
522
523
  # Examples for using set functions:
523
524
 
524
525
  #await demo_set_charger(vehicle, action = "start") # action = "start" or "stop"
525
- #await demo_set_charger_current(value='reduced') # value = 1-255/Maximum/Reduced (PHEV: 252 for reduced and 254 for max, EV: Maximum/Reduced)
526
+ #await demo_set_charger_current(vehicle, value='reduced') # value = 1-255/Maximum/Reduced (PHEV: 252 for reduced and 254 for max, EV: Maximum/Reduced)
526
527
 
527
528
  #await demo_set_climatisation(vehicle, action = "start", temp=18.0) # action = "auxilliary", "electric" or "off". spin is S-PIN and only needed for aux heating
528
529
  #await demo_set_climatisation_temp(vehicle, temp = 18.0) # temp = integer from 16 to 30
@@ -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.12"
6
+ __version__ = "0.0.14"
@@ -108,11 +108,12 @@ TIMEOUT = timedelta(seconds=90)
108
108
  class Connection:
109
109
  """ Connection to Connect services """
110
110
  # Init connection class
111
- def __init__(self, session, brand='cupra', username='', password='', fulldebug=False, **optional):
111
+ def __init__(self, session, brand='cupra', username='', password='', fulldebug=False, nightlyUpdateReduction=False, **optional):
112
112
  """ Initialize """
113
113
  self._session = session
114
114
  self._lock = asyncio.Lock()
115
115
  self._session_fulldebug = fulldebug
116
+ self._session_nightlyUpdateReduction = nightlyUpdateReduction
116
117
  self._session_headers = HEADERS_SESSION.get(brand).copy()
117
118
  self._session_base = BASE_SESSION
118
119
  self._session_auth_headers = HEADERS_AUTH.copy()
@@ -1369,6 +1370,8 @@ class Connection:
1369
1370
  if mode in {'start', 'stop'}:
1370
1371
  capability='charging'
1371
1372
  return await self._setViaAPI(eval(f"f'{API_REQUESTS}/{mode}'"))
1373
+ elif mode=='settings':
1374
+ return await self._setViaAPI(eval(f"f'{API_CHARGING}/{mode}'"), json=data)
1372
1375
  else:
1373
1376
  _LOGGER.error(f'Not yet implemented. Mode: {mode}. Command ignored')
1374
1377
  raise
@@ -125,6 +125,8 @@ AUTH_TOKENKEYS = 'https://identity.vwgroup.io/oidc/v1/keys'
125
125
  # API endpoints
126
126
  API_MBB_STATUSDATA = 'https://customer-profile.vwgroup.io/v3/customers/{self._user_id}/mbbStatusData'
127
127
  API_PERSONAL_DATA= 'https://customer-profile.vwgroup.io/v3/customers/{self._user_id}/personalData'
128
+ #Other option for personal data is '{baseurl}/v1/users/{self._user_id}'
129
+
128
130
  API_VEHICLES = '{APP_URI}/v2/users/{self._user_id}/garage/vehicles' # Garage info
129
131
  API_MYCAR = '{baseurl}/v5/users/{self._user_id}/vehicles/{vin}/mycar' # Vehicle status report
130
132
  API_CHARGING = '{baseurl}/v1/vehicles/{vin}/charging' # Vehicle charging information
@@ -933,6 +933,34 @@ class RequestResults(Sensor):
933
933
  def attributes(self):
934
934
  return dict(self.vehicle.request_results)
935
935
 
936
+ class ChargingState(BinarySensor):
937
+ def __init__(self):
938
+ super().__init__(attr="charging_state", name="Charging state", icon="mdi:battery-charging", device_class='power')
939
+
940
+ @property
941
+ def state(self):
942
+ return self.vehicle.charging_state
943
+
944
+ @property
945
+ def assumed_state(self):
946
+ return False
947
+
948
+ @property
949
+ def attributes(self):
950
+ attr = {}
951
+ #state = self.vehicle.attrs.get('charging', {}).get('status', {}).get('state', '')
952
+ #type = self.vehicle.attrs.get('charging', {}).get('status', {}).get('charging', {}).get('type', '')
953
+ #mode = self.vehicle.attrs.get('charging', {}).get('status', {}).get('charging', {}).get('mode', '')
954
+ state = self.vehicle.attrs.get('mycar', {}).get('services', {}).get('charging', {}).get('status', '')
955
+ type = self.vehicle.attrs.get('charging', {}).get('status', {}).get('charging', {}).get('type', '')
956
+ mode = self.vehicle.attrs.get('mycar', {}).get('services', {}).get('charging', {}).get('chargeMode', '')
957
+ if state in {'charging','Charging', 'conservation','Conservation'}:
958
+ attr['state']=state.lower()
959
+ if type != '':
960
+ attr['type']=type
961
+ if mode != '':
962
+ attr['mode']=mode
963
+ return attr
936
964
 
937
965
  def create_instruments():
938
966
  return [
@@ -960,6 +988,7 @@ def create_instruments():
960
988
  DepartureProfile1(),
961
989
  DepartureProfile2(),
962
990
  DepartureProfile3(),
991
+ ChargingState(),
963
992
  Sensor(
964
993
  attr="distance",
965
994
  name="Odometer",
@@ -1084,8 +1113,8 @@ def create_instruments():
1084
1113
  attr="charge_max_ampere",
1085
1114
  name="Charger max ampere",
1086
1115
  icon="mdi:flash",
1087
- unit="A",
1088
- device_class="current"
1116
+ #unit="A",
1117
+ #device_class="current"
1089
1118
  ),
1090
1119
  Sensor(
1091
1120
  attr="climatisation_target_temperature",
@@ -1284,11 +1313,11 @@ def create_instruments():
1284
1313
  name="Energy flow",
1285
1314
  device_class="power"
1286
1315
  ),
1287
- BinarySensor(
1288
- attr="charging_state",
1289
- name="Charging state",
1290
- device_class="power"
1291
- ),
1316
+ #BinarySensor(
1317
+ # attr="charging_state",
1318
+ # name="Charging state",
1319
+ # device_class="power"
1320
+ #),
1292
1321
  BinarySensor(
1293
1322
  attr="parking_light",
1294
1323
  name="Parking light",
@@ -129,9 +129,20 @@ class Vehicle:
129
129
  # Fetch all data if car is not deactivated
130
130
  if not self.deactivated:
131
131
  try:
132
+ if self._connection._session_nightlyUpdateReduction:
133
+ # nightlyUpdateReduction is activated
134
+ if datetime.now(tz=None).hour<5 or datetime.now(tz=None).hour>=22:
135
+ # current time is within the night interval
136
+ fullUpdateExpired = datetime.now(tz=None) - timedelta(seconds= 1100)
137
+ if hasattr(self, '_last_full_update'):
138
+ _LOGGER.debug(f'last_full_update= {self._last_full_update}, fullUpdateExpired= {fullUpdateExpired}.')
139
+ if updateType!=1 and (hasattr(self, '_last_full_update') and self._last_full_update>fullUpdateExpired):
140
+ _LOGGER.debug('Nightly update reduction is active and current time within 22:00 and 5:00. So we skip small update.')
141
+ return True
142
+
132
143
  # Data to be updated most often
133
144
  await asyncio.gather(
134
- self.get_charger(),
145
+ #self.get_charger(),
135
146
  self.get_basiccardata(),
136
147
  self.get_statusreport(),
137
148
  return_exceptions=True
@@ -146,6 +157,8 @@ class Vehicle:
146
157
 
147
158
  # Data to be updated less often
148
159
  await asyncio.gather(
160
+ #self.get_statusreport(),
161
+ self.get_charger(),
149
162
  self.get_preheater(),
150
163
  self.get_climater(),
151
164
  self.get_trip_statistic(),
@@ -320,7 +333,11 @@ class Vehicle:
320
333
  if 1 <= int(value) <= 255:
321
334
  # VW-Group API charger current request
322
335
  if self._relevantCapabilties.get('charging', {}).get('active', False):
323
- data = {'action': {'settings': {'maxChargeCurrentAC': int(value)}, 'type': 'setSettings'}}
336
+ data = {'maxChargeCurrentAc': int(value)}
337
+ if int(value)==252:
338
+ data = {'maxChargeCurrentAc': 'reduced'}
339
+ if int(value)==254:
340
+ data = {'maxChargeCurrentAc': 'maximum'}
324
341
  else:
325
342
  _LOGGER.error(f'Set charger maximum current to {value} is not supported.')
326
343
  raise SeatInvalidRequestException(f'Set charger maximum current to {value} is not supported.')
@@ -330,9 +347,7 @@ class Vehicle:
330
347
  # VW-Group API charger current request
331
348
  if self._relevantCapabilties.get('charging', {}).get('active', False):
332
349
  value = 'maximum' if value in ['Maximum', 'maximum', 'Max', 'max'] else 'reduced'
333
- data = {'settings':
334
- {'maxChargeCurrentAC': value}
335
- }
350
+ data = {'maxChargeCurrentAc': value}
336
351
  else:
337
352
  _LOGGER.error(f'Set charger maximum current to {value} is not supported.')
338
353
  raise SeatInvalidRequestException(f'Set charger maximum current to {value} is not supported.')
@@ -344,7 +359,7 @@ class Vehicle:
344
359
  _LOGGER.error('No charger support.')
345
360
  raise SeatInvalidRequestException('No charger support.')
346
361
 
347
- async def set_charger(self, action, **data):
362
+ async def set_charger(self, action, data=None):
348
363
  """Charging actions."""
349
364
  if not self._relevantCapabilties.get('charging', {}).get('active', False):
350
365
  _LOGGER.info('Remote start/stop of charger is not supported.')
@@ -361,7 +376,7 @@ class Vehicle:
361
376
  mode='start'
362
377
  elif action in ['stop', 'Stop', 'Off', 'off']:
363
378
  mode='stop'
364
- elif isinstance(action.get('action', None), dict):
379
+ elif action=='settings':
365
380
  mode=action
366
381
  else:
367
382
  _LOGGER.error(f'Invalid charger action: {action}. Must be either start, stop or setSettings')
@@ -386,6 +401,7 @@ class Vehicle:
386
401
  while not actionSuccessful and retry < 2:
387
402
  await asyncio.sleep(15)
388
403
  await self.get_charger()
404
+ await self.get_basiccardata() # We get both, get_charger() and get_basiccardata()
389
405
  if mode == 'start':
390
406
  if self.charging:
391
407
  actionSuccessful = True
@@ -393,7 +409,7 @@ class Vehicle:
393
409
  if not self.charging:
394
410
  actionSuccessful = True
395
411
  elif mode == 'settings':
396
- if data.get('settings',0).get('maxChargeCurrentAC','') == self.charge_max_ampere:
412
+ if data.get('maxChargeCurrentAc','') == self.charge_max_ampere:
397
413
  actionSuccessful = True
398
414
  else:
399
415
  _LOGGER.error(f'Missing code in vehicle._set_charger() for mode {mode}')
@@ -1473,16 +1489,22 @@ class Vehicle:
1473
1489
  @property
1474
1490
  def charging(self):
1475
1491
  """Return battery level"""
1476
- cstate = self.attrs.get('charging').get('status').get('charging').get('state','')
1492
+ #cstate = self.attrs.get('charging').get('status').get('charging').get('state','')
1493
+ cstate = self.attrs.get('mycar',{}).get('services',{}).get('charging',{}).get('status','')
1477
1494
  return 1 if cstate in ['charging', 'Charging'] else 0
1478
1495
 
1479
1496
  @property
1480
1497
  def is_charging_supported(self):
1481
1498
  """Return true if charging is supported"""
1482
- if self.attrs.get('charging', False):
1483
- if 'status' in self.attrs.get('charging', {}):
1484
- if 'charging' in self.attrs.get('charging')['status']:
1485
- if 'state' in self.attrs.get('charging')['status']['charging']:
1499
+ #if self.attrs.get('charging', False):
1500
+ # if 'status' in self.attrs.get('charging', {}):
1501
+ # if 'charging' in self.attrs.get('charging')['status']:
1502
+ # if 'state' in self.attrs.get('charging')['status']['charging']:
1503
+ # return True
1504
+ if self.attrs.get('mycar', False):
1505
+ if 'services' in self.attrs.get('mycar', {}):
1506
+ if 'charging' in self.attrs.get('mycar')['services']:
1507
+ if 'status' in self.attrs.get('mycar')['services']['charging']:
1486
1508
  return True
1487
1509
  return False
1488
1510
 
@@ -1504,26 +1526,33 @@ class Vehicle:
1504
1526
  @property
1505
1527
  def battery_level(self):
1506
1528
  """Return battery level"""
1507
- if self.attrs.get('charging', False):
1508
- return int(self.attrs.get('charging').get('status', {}).get('battery', {}).get('currentSocPercentage', 0))
1529
+ #if self.attrs.get('charging', False):
1530
+ # return int(self.attrs.get('charging').get('status', {}).get('battery', {}).get('currentSocPercentage', 0))
1531
+ if self.attrs.get('mycar', False):
1532
+ return int(self.attrs.get('mycar',{}).get('services', {}).get('charging', {}).get('currentPct', 0))
1509
1533
  else:
1510
1534
  return 0
1511
1535
 
1512
1536
  @property
1513
1537
  def is_battery_level_supported(self):
1514
1538
  """Return true if battery level is supported"""
1515
- if self.attrs.get('charging', False):
1516
- if 'status' in self.attrs.get('charging'):
1517
- if 'battery' in self.attrs.get('charging')['status']:
1518
- if 'currentSocPercentage' in self.attrs.get('charging')['status']['battery']:
1539
+ #if self.attrs.get('charging', False):
1540
+ # if 'status' in self.attrs.get('charging'):
1541
+ # if 'battery' in self.attrs.get('charging')['status']:
1542
+ # if 'currentSocPercentage' in self.attrs.get('charging')['status']['battery']:
1543
+ # return True
1544
+ if self.attrs.get('mycar', False):
1545
+ if 'services' in self.attrs.get('mycar'):
1546
+ if 'charging' in self.attrs.get('mycar')['services']:
1547
+ if 'currentPct' in self.attrs.get('mycar')['services']['charging']:
1519
1548
  return True
1520
1549
  return False
1521
1550
 
1522
1551
  @property
1523
1552
  def charge_max_ampere(self):
1524
1553
  """Return charger max ampere setting."""
1525
- if self.attrs.get('charger', False):
1526
- return self.attrs.get('charger').get('info').get('settings').get('maxChargeCurrentAC')
1554
+ if self.attrs.get('charging', False):
1555
+ return self.attrs.get('charging').get('info').get('settings').get('maxChargeCurrentAc')
1527
1556
  return 0
1528
1557
 
1529
1558
  @property
@@ -1532,7 +1561,7 @@ class Vehicle:
1532
1561
  if self.attrs.get('charging', False):
1533
1562
  if 'info' in self.attrs.get('charging', {}):
1534
1563
  if 'settings' in self.attrs.get('charging')['info']:
1535
- if 'maxChargeCurrentAC' in self.attrs.get('charging', {})['info']['settings']:
1564
+ if 'maxChargeCurrentAc' in self.attrs.get('charging', {})['info']['settings']:
1536
1565
  return True
1537
1566
  return False
1538
1567
 
@@ -1575,9 +1604,12 @@ class Vehicle:
1575
1604
  @property
1576
1605
  def charging_time_left(self):
1577
1606
  """Return minutes to charging complete"""
1578
- if self.external_power:
1579
- if self.attrs.get('charging', {}).get('status', {}).get('charging', {}).get('remainingTimeInMinutes', False):
1580
- minutes = int(self.attrs.get('charging', {}).get('status', {}).get('charging', {}).get('remainingTimeInMinutes', 0))
1607
+ #if self.external_power:
1608
+ if self.charging:
1609
+ #if self.attrs.get('charging', {}).get('status', {}).get('charging', {}).get('remainingTimeInMinutes', False):
1610
+ # minutes = int(self.attrs.get('charging', {}).get('status', {}).get('charging', {}).get('remainingTimeInMinutes', 0))
1611
+ if self.attrs.get('mycar', {}).get('services', {}).get('charging', {}).get('remainingTime', False):
1612
+ minutes = int(self.attrs.get('mycar', {}).get('services', {}).get('charging', {}).get('remainingTime', 0))
1581
1613
  else:
1582
1614
  minutes = 0
1583
1615
  return minutes
@@ -1644,22 +1676,43 @@ class Vehicle:
1644
1676
  return True
1645
1677
 
1646
1678
  @property
1647
- #def energy_flow(self):
1648
- # """Return true if energy is flowing through charging port."""
1649
1679
  def charging_state(self):
1650
1680
  """Return true if vehicle is charging."""
1651
- check = self.attrs.get('charging', {}).get('status', {}).get('state', '')
1652
- if check == 'charging':
1681
+ #check = self.attrs.get('charging', {}).get('status', {}).get('state', '')
1682
+ check = self.attrs.get('mycar',{}).get('services',{}).get('charging',{}).get('status','')
1683
+ if check in ('charging','Charging'):
1653
1684
  return True
1654
1685
  else:
1655
1686
  return False
1656
1687
 
1657
1688
  @property
1658
- #def is_energy_flow_supported(self):
1659
- # """Energy flow supported."""
1660
1689
  def is_charging_state_supported(self):
1661
1690
  """Charging state supported."""
1662
- if self.attrs.get('charging', {}).get('status', {}).get('state', False):
1691
+ #if self.attrs.get('charging', {}).get('status', {}).get('state', False):
1692
+ # return True
1693
+ if self.attrs.get('mycar', False):
1694
+ if 'services' in self.attrs.get('mycar', {}):
1695
+ if 'charging' in self.attrs.get('mycar')['services']:
1696
+ if 'status' in self.attrs.get('mycar')['services']['charging']:
1697
+ return True
1698
+
1699
+ @property
1700
+ def energy_flow(self):
1701
+ """Return true if energy is flowing to (i.e. charging) or from (i.e. climating with battery power) the battery."""
1702
+ if self.charging_state:
1703
+ return True
1704
+ #check = self.attrs.get('charging', {}).get('status', {}).get('state', '')
1705
+ check = self.attrs.get('mycar',{}).get('services',{}).get('charging',{}).get('status','')
1706
+ if self.is_electric_climatisation_supported:
1707
+ if self.electric_climatisation and check not in {'charging','Charging', 'conservation','Conservation'}:
1708
+ # electric climatisation is on and car is not charging or conserving power
1709
+ return True
1710
+ return False
1711
+
1712
+ @property
1713
+ def is_energy_flow_supported(self):
1714
+ """Energy flow supported."""
1715
+ if self.is_charging_state_supported:
1663
1716
  return True
1664
1717
 
1665
1718
  # Vehicle location states
@@ -2907,7 +2960,8 @@ class Vehicle:
2907
2960
  @property
2908
2961
  def is_request_results_supported(self):
2909
2962
  """Request results is supported if in progress is supported."""
2910
- return self.is_request_in_progress_supported
2963
+ return False # deactivated because it provides no usefull information
2964
+ #return self.is_request_in_progress_supported
2911
2965
 
2912
2966
  @property
2913
2967
  def requests_remaining(self):
@@ -2923,8 +2977,9 @@ class Vehicle:
2923
2977
 
2924
2978
  @property
2925
2979
  def is_requests_remaining_supported(self):
2926
- if self.is_request_in_progress_supported:
2927
- return True if self._requests.get('remaining', False) else False
2980
+ return False # deactivated because it provides no usefull information
2981
+ #if self.is_request_in_progress_supported:
2982
+ # return True if self._requests.get('remaining', False) else False
2928
2983
 
2929
2984
  #### Helper functions ####
2930
2985
  def __str__(self):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycupra
3
- Version: 0.0.12
3
+ Version: 0.0.14
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes