pycupra 0.0.11__tar.gz → 0.0.13__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.
@@ -6,4 +6,5 @@ dist/
6
6
  __pycache__/
7
7
  *.json
8
8
  *.txt
9
+ *.log
9
10
  www/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycupra
3
- Version: 0.0.11
3
+ Version: 0.0.13
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
@@ -48,6 +48,7 @@ RESOURCES = [
48
48
  "charging",
49
49
  "charge_rate",
50
50
  "charging_power",
51
+ "charging_state",
51
52
  "charging_cable_connected",
52
53
  "charging_cable_locked",
53
54
  "charging_time_left",
@@ -204,9 +205,28 @@ async def demo_set_timer_schedule(vehicle):
204
205
  print(" Request failed.")
205
206
  return success
206
207
 
208
+ async def demo_set_departure_profile_schedule(vehicle):
209
+ print('########################################')
210
+ print('# Change one departure profile #')
211
+ print('########################################')
212
+ success= await vehicle.set_departure_profile_schedule(id = 3, # id = 1, 2, 3
213
+ schedule = { # Set the departure time, date and periodicity
214
+ "enabled": True, # Set the timer active or not, True or False, required
215
+ "recurring": True, # True or False for recurring, required
216
+ "time": "12:34", # Time for departure, required
217
+ "days": "nyynnnn", # Days (mon-sun) for recurring schedule (n=disable, y=enable), required if recurring=True
218
+ "chargingProgramId": 2, # Id of the charging program to be used for the departure profile
219
+ }
220
+ )
221
+ if success:
222
+ print(" Request completed successfully.")
223
+ else:
224
+ print(" Request failed.")
225
+ return success
226
+
207
227
  async def demo_set_timer_active(vehicle, id=1, action="off"):
208
228
  print('########################################')
209
- print('# Active/Deactivate one timer #')
229
+ print('# (De-)Activate one timer #')
210
230
  print('########################################')
211
231
  success= await vehicle.set_timer_active(id, action) # id = 1, 2, 3, action = "on" or "off".
212
232
  if success:
@@ -215,6 +235,17 @@ async def demo_set_timer_active(vehicle, id=1, action="off"):
215
235
  print(" Request failed.")
216
236
  return success
217
237
 
238
+ async def demo_set_departure_profile_active(vehicle, id=1, action="off"):
239
+ print('########################################')
240
+ print('# (De-)Activate one departure profile #')
241
+ print('########################################')
242
+ success= await vehicle.set_departure_profile_active(id, action) # id = 1, 2, 3, action = "on" or "off".
243
+ if success:
244
+ print(" Request completed successfully.")
245
+ else:
246
+ print(" Request failed.")
247
+ return success
248
+
218
249
  async def demo_set_charge_limit(vehicle, limit=30):
219
250
  print('########################################')
220
251
  print('# Change minimum charge limit #')
@@ -502,6 +533,9 @@ async def main():
502
533
  #await demo_set_timer_active(vehicle, id=3, action="off") # id = 1, 2, 3, action = "on" or "off".
503
534
  #await demo_set_charge_limit(vehicle, 30) # limit = PHEV: 0/10/20/30/40/50, EV: 50/60/70/80/90/100
504
535
 
536
+ #await demo_set_departure_profile_schedule(vehicle) # arguments id and schedule can be found in the demo function
537
+ #await demo_set_departure_profile_active(vehicle, id=3, action="off") # id = 1, 2, 3, action = "on" or "off".
538
+
505
539
  #await demo_set_lock(vehicle,action = "lock",
506
540
  # spin = credentials.get('spin','')) # action = "unlock" or "lock". spin = SPIN, needed for both
507
541
 
@@ -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.11"
6
+ __version__ = "0.0.13"
@@ -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,31 @@ 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
+ if state in {'charging', 'conservation'}:
955
+ attr['state']=state
956
+ if type != '':
957
+ attr['type']=type
958
+ if mode != '':
959
+ attr['mode']=mode
960
+ return attr
936
961
 
937
962
  def create_instruments():
938
963
  return [
@@ -960,6 +985,7 @@ def create_instruments():
960
985
  DepartureProfile1(),
961
986
  DepartureProfile2(),
962
987
  DepartureProfile3(),
988
+ ChargingState(),
963
989
  Sensor(
964
990
  attr="distance",
965
991
  name="Odometer",
@@ -1284,6 +1310,11 @@ def create_instruments():
1284
1310
  name="Energy flow",
1285
1311
  device_class="power"
1286
1312
  ),
1313
+ #BinarySensor(
1314
+ # attr="charging_state",
1315
+ # name="Charging state",
1316
+ # device_class="power"
1317
+ #),
1287
1318
  BinarySensor(
1288
1319
  attr="parking_light",
1289
1320
  name="Parking light",
@@ -471,7 +471,7 @@ class Vehicle:
471
471
  raise SeatInvalidRequestException('Departure timers are not supported.')
472
472
 
473
473
  async def set_timer_schedule(self, id, schedule={}):
474
- """ Set departure schedules. """
474
+ """ Set departure timer schedule. """
475
475
  data = {}
476
476
  # Validate required user inputs
477
477
  supported = "is_departure" + str(id) + "_supported"
@@ -518,7 +518,7 @@ class Vehicle:
518
518
  if not 16 <= int(schedule.get("targetTemp", None)) <= 30:
519
519
  raise SeatInvalidRequestException('Target temp must be integer value from 16 to 30')
520
520
  else:
521
- data['temp'] = schedule.get('targetTemp')
521
+ data['temp'] = int(schedule.get('targetTemp'))
522
522
  raise SeatInvalidRequestException('Target temp (yet) not supported.')
523
523
 
524
524
  # Validate charge target and current
@@ -666,6 +666,98 @@ class Vehicle:
666
666
  self._requests['departuretimer'] = {'status': 'Exception'}
667
667
  raise SeatException('Failed to set departure timer schedule')
668
668
 
669
+ async def set_departure_profile_schedule(self, id, schedule={}):
670
+ """ Set departure profile schedule. """
671
+ data = {}
672
+ # Validate required user inputs
673
+ supported = "is_departure_profile" + str(id) + "_supported"
674
+ if getattr(self, supported) is not True:
675
+ raise SeatConfigException(f'Departure profile id {id} is not supported for this vehicle.')
676
+ else:
677
+ _LOGGER.debug(f'Departure profile id {id} is supported')
678
+ if not schedule:
679
+ raise SeatInvalidRequestException('A schedule must be set.')
680
+ if not isinstance(schedule.get('enabled', ''), bool):
681
+ raise SeatInvalidRequestException('The enabled variable must be set to True or False.')
682
+ if not isinstance(schedule.get('recurring', ''), bool):
683
+ raise SeatInvalidRequestException('The recurring variable must be set to True or False.')
684
+ if not re.match('^[0-9]{2}:[0-9]{2}$', schedule.get('time', '')):
685
+ raise SeatInvalidRequestException('The time for departure must be set in 24h format HH:MM.')
686
+
687
+ # Validate optional inputs
688
+ if schedule.get('recurring', False):
689
+ if not re.match('^[yn]{7}$', schedule.get('days', '')):
690
+ raise SeatInvalidRequestException('For recurring schedules the days variable must be set to y/n mask (mon-sun with only wed enabled): nnynnnn.')
691
+ elif not schedule.get('recurring'):
692
+ if not re.match('^[0-9]{4}-[0-9]{2}-[0-9]{2}$', schedule.get('date', '')):
693
+ raise SeatInvalidRequestException('For single departure profile schedule the date variable must be set to YYYY-mm-dd.')
694
+
695
+ if self._relevantCapabilties.get('departureProfiles', {}).get('active', False):
696
+ # Check if profileIds is set and correct
697
+ if schedule.get('chargingProgramId', False):
698
+ # At the moment, only one charging program id is supported
699
+ chargingProgramId = int(schedule.get('chargingProgramId', False))
700
+ found = False
701
+ for chargingProgram in self.attrs.get('departureProfiles', {}).get('profileIds', []):
702
+ if chargingProgram.get('id',None) == chargingProgramId:
703
+ found = True
704
+ break
705
+ if not found:
706
+ raise SeatInvalidRequestException('The charging program id provided for the departure profile schedule is unknown.')
707
+ else:
708
+ profileIds = []
709
+ profileIds.append(chargingProgramId)
710
+ else:
711
+ raise SeatInvalidRequestException('No charging program id provided for departure profile schedule.')
712
+
713
+ newDepProfileSchedule = {}
714
+ # Prepare data and execute
715
+ newDepProfileSchedule['id'] = id
716
+ # Converting schedule to data map
717
+ if schedule.get("enabled",False):
718
+ newDepProfileSchedule['enabled']=True
719
+ else:
720
+ newDepProfileSchedule['enabled']=False
721
+ if schedule.get("recurring",False):
722
+ newDepProfileSchedule['recurringTimer']= {
723
+ "startTime": schedule.get('time',"00:00"),
724
+ "recurringOn":{""
725
+ "mondays":(schedule.get('days',"nnnnnnn")[0]=='y'),
726
+ "tuesdays":(schedule.get('days',"nnnnnnn")[1]=='y'),
727
+ "wednesdays":(schedule.get('days',"nnnnnnn")[2]=='y'),
728
+ "thursdays":(schedule.get('days',"nnnnnnn")[3]=='y'),
729
+ "fridays":(schedule.get('days',"nnnnnnn")[4]=='y'),
730
+ "saturdays":(schedule.get('days',"nnnnnnn")[5]=='y'),
731
+ "sundays":(schedule.get('days',"nnnnnnn")[6]=='y'),
732
+ }
733
+ }
734
+ else:
735
+ if self._relevantCapabilties.get('departureProfiles', {}).get('supportsSingleTimer', False):
736
+ startDateTime = datetime.fromisoformat(schedule.get('date',"2025-01-01")+'T'+schedule.get('time',"00:00"))
737
+ _LOGGER.info(f'startDateTime={startDateTime.isoformat()}')
738
+ newDepProfileSchedule['singleTimer']= {
739
+ "startDateTimeLocal": startDateTime.isoformat(),
740
+ }
741
+ else:
742
+ raise SeatInvalidRequestException('Vehicle does not support single timer.')
743
+ newDepProfileSchedule["profileIds"]= profileIds
744
+
745
+ # Now we have to substitute the current departure profile schedule with the given id by the new one
746
+ data= deepcopy(self.attrs.get('departureProfiles'))
747
+ if len(data.get('timers', []))<1:
748
+ raise SeatInvalidRequestException(f'No timers found in departure profile: {data}.')
749
+ idFound=False
750
+ for e in range(len(data.get('timers', []))):
751
+ if data['timers'][e].get('id',-1)==id:
752
+ data['timers'][e] = newDepProfileSchedule
753
+ idFound=True
754
+ if idFound:
755
+ return await self._set_departure_profiles(data, action='set')
756
+ raise SeatInvalidRequestException(f'Departure profile id {id} not found in {data.get('timers',[])}.')
757
+ else:
758
+ _LOGGER.info('Departure profiles are not supported.')
759
+ raise SeatInvalidRequestException('Departure profiles are not supported.')
760
+
669
761
  async def set_departure_profile_active(self, id=1, action='off'):
670
762
  """ Activate/deactivate departure profiles. """
671
763
  data = {}
@@ -1552,18 +1644,36 @@ class Vehicle:
1552
1644
  return True
1553
1645
 
1554
1646
  @property
1555
- def energy_flow(self):
1556
- """Return true if energy is flowing through charging port."""
1557
- check = self.attrs.get('charger', {}).get('status', {}).get('chargingStatusData', {}).get('energyFlow', {}).get('content', 'off')
1558
- if check == 'on':
1647
+ def charging_state(self):
1648
+ """Return true if vehicle is charging."""
1649
+ check = self.attrs.get('charging', {}).get('status', {}).get('state', '')
1650
+ if check == 'charging':
1559
1651
  return True
1560
1652
  else:
1561
1653
  return False
1562
1654
 
1655
+ @property
1656
+ def is_charging_state_supported(self):
1657
+ """Charging state supported."""
1658
+ if self.attrs.get('charging', {}).get('status', {}).get('state', False):
1659
+ return True
1660
+
1661
+ @property
1662
+ def energy_flow(self):
1663
+ """Return true if energy is flowing to (i.e. charging) or from (i.e. climating with battery power) the battery."""
1664
+ if self.charging_state:
1665
+ return True
1666
+ check = self.attrs.get('charging', {}).get('status', {}).get('state', '')
1667
+ if self.is_electric_climatisation_supported:
1668
+ if self.electric_climatisation and check not in {'charging', 'conservation'}:
1669
+ # electric climatisation is on and car is not charging or conserving power
1670
+ return True
1671
+ return False
1672
+
1563
1673
  @property
1564
1674
  def is_energy_flow_supported(self):
1565
1675
  """Energy flow supported."""
1566
- if self.attrs.get('charger', {}).get('status', {}).get('chargingStatusData', {}).get('energyFlow', False):
1676
+ if self.is_charging_state_supported:
1567
1677
  return True
1568
1678
 
1569
1679
  # Vehicle location states
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycupra
3
- Version: 0.0.11
3
+ Version: 0.0.13
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