pycupra 0.1.11__py3-none-any.whl → 0.1.13__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.
example/PyCupra.py ADDED
@@ -0,0 +1,603 @@
1
+ #!/usr/bin/env python3
2
+ """ Sample program to show the features of pycupra"""
3
+ import asyncio
4
+ import logging
5
+ import inspect
6
+ import sys
7
+ import os
8
+ import json
9
+ import pandas as pd
10
+ from aiohttp import ClientSession
11
+ from datetime import datetime
12
+
13
+ currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
14
+ parentdir = os.path.dirname(currentdir)
15
+ sys.path.insert(0, parentdir)
16
+
17
+ try:
18
+ from pycupra import Connection
19
+ except ModuleNotFoundError as e:
20
+ print(f"Unable to import library: {e}")
21
+ sys.exit(1)
22
+
23
+ logging.basicConfig(level=logging.DEBUG)
24
+ _LOGGER = logging.getLogger(__name__)
25
+ BRAND = 'cupra' # or 'seat' (Change it to 'seat' if you want to connect to the My Seat portal)
26
+
27
+ PRINTRESPONSE = True
28
+ INTERVAL = 5
29
+ TOKEN_FILE_NAME_AND_PATH='./pycupra_token.json'
30
+ CREDENTIALS_FILE_NAME_AND_PATH='./pycupra_credentials.json'
31
+ FIREBASE_CREDENTIALS_FILE_NAME_AND_PATH='./pycupra_firebase_credentials.json'
32
+ ALL_ATTRIBUTES_FILE_NAME_AND_PATH='./pycupra_all_attributes.txt'
33
+
34
+
35
+ COMPONENTS = {
36
+ 'sensor': 'sensor',
37
+ 'binary_sensor': 'binary_sensor',
38
+ 'lock': 'lock',
39
+ 'device_tracker': 'device_tracker',
40
+ 'switch': 'switch',
41
+ }
42
+
43
+ RESOURCES = [
44
+ "adblue_level",
45
+ "area_alarm",
46
+ "auxiliary_climatisation",
47
+ "battery_level",
48
+ "charge_max_ampere",
49
+ "charger_action_status",
50
+ "charging",
51
+ "charge_max_ampere",
52
+ "charge_rate",
53
+ "charging_power",
54
+ "charging_state",
55
+ "charging_cable_connected",
56
+ "charging_cable_locked",
57
+ "charging_time_left",
58
+ "climater_action_status",
59
+ "climatisation_target_temperature",
60
+ "climatisation_without_external_power",
61
+ "combined_range",
62
+ "combustion_range",
63
+ "departure1",
64
+ "departure2",
65
+ "departure3",
66
+ "departure_profile1",
67
+ "departure_profile2",
68
+ "departure_profile3",
69
+ "distance",
70
+ "door_closed_left_back",
71
+ "door_closed_left_front",
72
+ "door_closed_right_back",
73
+ "door_closed_right_front",
74
+ "door_locked",
75
+ "electric_climatisation",
76
+ "electric_range",
77
+ "energy_flow",
78
+ "external_power",
79
+ "fuel_level",
80
+ "hood_closed",
81
+ "last_connected",
82
+ "last_full_update",
83
+ "lock_action_status",
84
+ "oil_inspection",
85
+ "oil_inspection_distance",
86
+ "outside_temperature",
87
+ "parking_light",
88
+ "parking_time",
89
+ "pheater_heating",
90
+ "pheater_status",
91
+ "pheater_ventilation",
92
+ "position",
93
+ "refresh_action_status",
94
+ "refresh_data",
95
+ "request_flash",
96
+ "request_honkandflash",
97
+ "request_in_progress",
98
+ "request_results",
99
+ "requests_remaining",
100
+ "service_inspection",
101
+ "service_inspection_distance",
102
+ "slow_charge",
103
+ "sunroof_closed",
104
+ "target_soc",
105
+ "trip_last_average_auxillary_consumption",
106
+ "trip_last_average_electric_consumption",
107
+ "trip_last_average_fuel_consumption",
108
+ "trip_last_average_speed",
109
+ "trip_last_duration",
110
+ "trip_last_entry",
111
+ "trip_last_length",
112
+ "trip_last_recuperation",
113
+ "trip_last_total_electric_consumption",
114
+ "trip_last_cycle_average_auxillary_consumption",
115
+ "trip_last_cycle_average_electric_consumption",
116
+ "trip_last_cycle_average_fuel_consumption",
117
+ "trip_last_cycle_average_speed",
118
+ "trip_last_cycle_duration",
119
+ "trip_last_cycle_entry",
120
+ "trip_last_cycle_length",
121
+ "trip_last_cycle_recuperation",
122
+ "trip_last_cycle_total_electric_consumption",
123
+ "trunk_closed",
124
+ "trunk_locked",
125
+ "vehicle_moving",
126
+ "warnings",
127
+ "window_closed_left_back",
128
+ "window_closed_left_front",
129
+ "window_closed_right_back",
130
+ "window_closed_right_front",
131
+ "window_heater",
132
+ "windows_closed",
133
+ "seat_heating"
134
+ ]
135
+
136
+ def is_enabled(attr):
137
+ """Return true if the user has enabled the resource."""
138
+ return attr in RESOURCES
139
+
140
+ def readCredentialsFile():
141
+ try:
142
+ with open(CREDENTIALS_FILE_NAME_AND_PATH, "r") as f:
143
+ credentialsString=f.read()
144
+ credentials=json.loads(credentialsString)
145
+ return credentials
146
+ except:
147
+ _LOGGER.info('readCredentialsFile not successful. Perhaps no credentials file present.')
148
+ return None
149
+
150
+ def exportToCSV(vehicle, csvFileName, dataType='short'):
151
+ df= pd.DataFrame(vehicle._states['tripstatistics'][dataType])
152
+ _LOGGER.debug('Exporting trip data to csv')
153
+ df.to_csv(csvFileName)
154
+ return True
155
+
156
+ def exportAllAttributes(vehicle, exportFileName):
157
+ try:
158
+ with open(exportFileName, "w") as f:
159
+ print(vehicle.attrs, file=f)
160
+ f.close()
161
+ return True
162
+ except Exception as e:
163
+ _LOGGER.warning(f'exportAllAttributes() not successful. Error: {e}')
164
+ return False
165
+
166
+ async def demo_set_charger(vehicle, action="start"):
167
+ print('########################################')
168
+ print('# Start/Stop charging #')
169
+ print('########################################')
170
+ success= await vehicle.set_charger(action) # mode = "start", "stop", "on" or "off".
171
+ if success:
172
+ print(" Request completed successfully.")
173
+ else:
174
+ print(" Request failed.")
175
+ return success
176
+
177
+ async def demo_set_charger_current(vehicle, value="reduced"):
178
+ print('########################################')
179
+ print('# Change charging current #')
180
+ print('########################################')
181
+ success= await vehicle.set_charger_current(value)
182
+ if success:
183
+ print(" Request completed successfully.")
184
+ else:
185
+ print(" Request failed.")
186
+ return success
187
+
188
+ async def demo_set_charger_target_soc(vehicle, value=80):
189
+ print('########################################')
190
+ print('# Change target state of charge #')
191
+ print('########################################')
192
+ success= await vehicle.set_charger_target_soc(value)
193
+ if success:
194
+ print(" Request completed successfully.")
195
+ else:
196
+ print(" Request failed.")
197
+ return success
198
+
199
+ async def demo_set_timer_schedule(vehicle):
200
+ print('########################################')
201
+ print('# Change one timer schedule #')
202
+ print('########################################')
203
+ success= await vehicle.set_timer_schedule(id = 3, # id = 1, 2, 3
204
+ schedule = { # Set the departure time, date and periodicity
205
+ "enabled": True, # Set the timer active or not, True or False, required
206
+ "recurring": False, # True or False for recurring, required
207
+ "date": "2025-03-21", # Date for departure, required if recurring=False
208
+ "time": "12:34", # Time for departure, required
209
+ "days": "nyynnnn", # Days (mon-sun) for recurring schedule (n=disable, y=enable), required if recurring=True
210
+ "nightRateActive": True, # True or False Off-peak hours, optional
211
+ "nightRateStart": "23:00", # Off-peak hours start (HH:mm), optional
212
+ "nightRateEnd": "06:00", # Off-peak hours end (HH:mm), optional
213
+ "operationCharging": True, # True or False for charging, optional
214
+ "operationClimatisation": False, # True or False fro climatisation, optional
215
+ "targetTemp": 22, # Target temperature for climatisation, optional
216
+ }
217
+ )
218
+ if success:
219
+ print(" Request completed successfully.")
220
+ else:
221
+ print(" Request failed.")
222
+ return success
223
+
224
+ async def demo_set_departure_profile_schedule(vehicle):
225
+ print('########################################')
226
+ print('# Change one departure profile #')
227
+ print('########################################')
228
+ success= await vehicle.set_departure_profile_schedule(id = 3, # id = 1, 2, 3
229
+ schedule = { # Set the departure time, date and periodicity
230
+ "enabled": True, # Set the timer active or not, True or False, required
231
+ "recurring": True, # True or False for recurring, required
232
+ "time": "12:34", # Time for departure, required
233
+ "days": "nyynnnn", # Days (mon-sun) for recurring schedule (n=disable, y=enable), required if recurring=True
234
+ "chargingProgramId": 2, # Id of the charging program to be used for the departure profile
235
+ }
236
+ )
237
+ if success:
238
+ print(" Request completed successfully.")
239
+ else:
240
+ print(" Request failed.")
241
+ return success
242
+
243
+ async def demo_set_timer_active(vehicle, id=1, action="off"):
244
+ print('########################################')
245
+ print('# (De-)Activate one timer #')
246
+ print('########################################')
247
+ success= await vehicle.set_timer_active(id, action) # id = 1, 2, 3, action = "on" or "off".
248
+ if success:
249
+ print(" Request completed successfully.")
250
+ else:
251
+ print(" Request failed.")
252
+ return success
253
+
254
+ async def demo_set_departure_profile_active(vehicle, id=1, action="off"):
255
+ print('########################################')
256
+ print('# (De-)Activate one departure profile #')
257
+ print('########################################')
258
+ success= await vehicle.set_departure_profile_active(id, action) # id = 1, 2, 3, action = "on" or "off".
259
+ if success:
260
+ print(" Request completed successfully.")
261
+ else:
262
+ print(" Request failed.")
263
+ return success
264
+
265
+ async def demo_set_charge_limit(vehicle, limit=30):
266
+ print('########################################')
267
+ print('# Change minimum charge limit #')
268
+ print('########################################')
269
+ success= await vehicle.set_charge_limit(limit) # limit = 0,10,20,30,40,50
270
+ if success:
271
+ print(" Request completed successfully.")
272
+ else:
273
+ print(" Request failed.")
274
+ return success
275
+
276
+ async def demo_set_climatisation(vehicle, action="start", temp=18.0):
277
+ print('########################################')
278
+ print('# Start/Stop climatisation #')
279
+ print('########################################')
280
+ success= await vehicle.set_climatisation(action, temp) # mode = "auxilliary", "electric" or "off". spin is S-PIN and only needed for aux heating
281
+ if success:
282
+ print(" Request completed successfully.")
283
+ else:
284
+ print(" Request failed.")
285
+ return success
286
+
287
+ async def demo_set_climatisation_temp(vehicle, temp=18.0):
288
+ print('########################################')
289
+ print('# Change climatisation temperature #')
290
+ print('########################################')
291
+ success= await vehicle.set_climatisation_temp(temp) # temperature = integer from 16 to 30
292
+ if success:
293
+ print(" Request completed successfully.")
294
+ else:
295
+ print(" Request failed.")
296
+ return success
297
+
298
+ async def demo_set_battery_climatisation(vehicle, mode=True):
299
+ print('########################################')
300
+ print('# Set battery climatisation setting #')
301
+ print('########################################')
302
+ success= await vehicle.set_battery_climatisation(mode) # mode = False or True
303
+ if success:
304
+ print(" Request completed successfully.")
305
+ else:
306
+ print(" Request failed.")
307
+ return success
308
+
309
+ async def demo_set_windowheating(vehicle, action="stop"):
310
+ print('########################################')
311
+ print('# Start/Stop window heating #')
312
+ print('########################################')
313
+ success= await vehicle.set_window_heating(action) # action = "start" or "stop"
314
+ if success:
315
+ print(" Request completed successfully.")
316
+ else:
317
+ print(" Request failed.")
318
+ return success
319
+
320
+ async def demo_set_honkandflash(vehicle, action="flash"):
321
+ print('########################################')
322
+ print('# Initiate (honk and) flash #')
323
+ print('########################################')
324
+ success= await vehicle.set_honkandflash(action)
325
+ if success:
326
+ print(" Request completed successfully.")
327
+ else:
328
+ print(" Request failed.")
329
+ return success
330
+
331
+ async def demo_set_lock(vehicle, action="lock", spin="1234"):
332
+ print('########################################')
333
+ print('# Lock/unlock vehicle #')
334
+ print('########################################')
335
+ success= await vehicle.set_lock(action, spin)
336
+ if success:
337
+ print(" Request completed successfully.")
338
+ else:
339
+ print(" Request failed.")
340
+ return success
341
+
342
+ async def demo_show_last_honkandflash_info(vehicle):
343
+ print('########################################')
344
+ print('# Show info of last honk&flash request #')
345
+ print('########################################')
346
+ status= vehicle.honkandflash_action_status
347
+ timestamp= vehicle.honkandflash_action_timestamp
348
+ if True: #success:
349
+ print(f" Last honk and flash was at {timestamp}. Status: {status}")
350
+ else:
351
+ print(" No honk and flash request was found.")
352
+ return timestamp
353
+
354
+ async def demo_send_destination(vehicle):
355
+ print('########################################')
356
+ print('# Send destination to vehicle #')
357
+ print('########################################')
358
+ success= await vehicle.send_destination(
359
+ destination = { # Send destination address
360
+ "address": { # address data optional
361
+ "city":"Weiterstadt",
362
+ "country":"Germany",
363
+ "stateAbbreviation":"Hessen",
364
+ "street":"Max-Planck-Straße",
365
+ "houseNumber":"3-5",
366
+ "zipCode":"64331"
367
+ },
368
+ "poiProvider":"google", # poiProvider mandatory
369
+ "geoCoordinate":{"latitude":49.89824,"longitude":8.59465}, # geoCoordinate mandatory
370
+ "destinationName":"Seat/Cupra Deutschland"
371
+ }
372
+ )
373
+ if success:
374
+ print(" Request completed successfully.")
375
+ else:
376
+ print(" Request failed.")
377
+ return success
378
+
379
+
380
+ async def main():
381
+ """Main method."""
382
+ credentials= readCredentialsFile()
383
+ if credentials==None or credentials.get('username','')=='' or (credentials.get('password','')==''):
384
+ _LOGGER.warning('Can not use the credentials read from the credentials file.')
385
+ raise
386
+ async with ClientSession(headers={'Connection': 'keep-alive'}) as session:
387
+ print('')
388
+ print('######################################################')
389
+ print('# Logging on to ola.prod.code.seat.cloud.vwgroup.com #')
390
+ print('######################################################')
391
+ print(f"Initiating new session to Cupra/Seat Cloud with {credentials.get('username')} as username")
392
+ connection = Connection(session, BRAND, credentials.get('username'), credentials.get('password'), PRINTRESPONSE, nightlyUpdateReduction=False, anonymise=True, tripStatisticsStartDate='1970-01-01')
393
+ print("Attempting to login to the Seat Cloud service")
394
+ print(datetime.now())
395
+ if await connection.doLogin(tokenFile=TOKEN_FILE_NAME_AND_PATH, apiKey=credentials.get('apiKey',None)):
396
+ print('Login or token refresh success!')
397
+ print(datetime.now())
398
+ print('Fetching user information for account.')
399
+ await connection.get_userData()
400
+ print(f"\tName: {connection._userData.get('name','')}")
401
+ print(f"\tNickname: {connection._userData.get('nickname','')}")
402
+ print(f"\tEmail: {connection._userData.get('email','')}")
403
+ print(f"\tPicture: {connection._userData.get('picture','')}")
404
+ print("")
405
+ print('Fetching vehicles associated with account.')
406
+ await connection.get_vehicles()
407
+
408
+ instruments = set()
409
+ for vehicle in connection.vehicles:
410
+ txt = vehicle.vin
411
+ if vehicle == connection.vehicles[0]: # Firebase can only be activated for one vehicle. So we use it for the first one
412
+ newStatus = await vehicle.initialiseFirebase(FIREBASE_CREDENTIALS_FILE_NAME_AND_PATH, vehicle.update)
413
+ print('########################################')
414
+ print('# Initialisation of firebase #')
415
+ print(txt.center(40, '#'))
416
+ print(f"New status of firebase={newStatus}")
417
+
418
+ print('')
419
+ print('########################################')
420
+ print('# Setting up dashboard #')
421
+ print(txt.center(40, '#'))
422
+ dashboard = vehicle.dashboard(mutable=True)
423
+
424
+ """for instrument in (
425
+ instrument
426
+ for instrument in dashboard.instruments
427
+ if instrument.component in COMPONENTS
428
+ and is_enabled(instrument.slug_attr)):
429
+
430
+ instruments.add(instrument)
431
+ """
432
+ for instrument in dashboard.instruments:
433
+ if instrument.component in COMPONENTS and is_enabled(instrument.slug_attr):
434
+ instruments.add(instrument)
435
+ print('')
436
+ print('########################################')
437
+ print('# Vehicles discovered #')
438
+ print('########################################')
439
+ for vehicle in connection.vehicles:
440
+ print(f"\tVIN: {vehicle.vin}")
441
+ print(f"\tModel: {vehicle.model}")
442
+ print(f"\tManufactured: {vehicle.model_year}")
443
+ print(f"\tConnect service deactivated: {vehicle.deactivated}")
444
+ print("")
445
+ if vehicle.is_nickname_supported: print(f"\tNickname: {vehicle.nickname}")
446
+ print(f"\tObject attributes, and methods:")
447
+ for prop in dir(vehicle):
448
+ if not "__" in prop:
449
+ try:
450
+ func = f"vehicle.{prop}"
451
+ typ = type(eval(func))
452
+ print(f"\t\t{prop} - {typ}")
453
+ except:
454
+ pass
455
+
456
+ else:
457
+ return False
458
+
459
+ # Output all instruments and states
460
+ print('')
461
+ print('########################################')
462
+ print('# Instruments from dashboard #')
463
+ print('########################################')
464
+ inst_list = sorted(instruments, key=lambda x: x.attr)
465
+ for instrument in inst_list:
466
+ print(f'{instrument.full_name}|{instrument.attr} - {instrument.str_state}|{instrument.state} - attributes: {instrument.attributes}')
467
+
468
+ print('')
469
+ print(f"Sleeping for {INTERVAL} seconds")
470
+ await asyncio.sleep(INTERVAL)
471
+
472
+ print('')
473
+ print(datetime.now())
474
+ print('')
475
+ #print('########################################')
476
+ #print('# Updating all values from MyCupra/Seat#')
477
+ #print('########################################')
478
+ #print("Updating ALL values from My Cupra/Seat Cloud ....")
479
+ #if await connection.update_all():
480
+ # print("Success!")
481
+ #else:
482
+ # print("Failed")
483
+
484
+ # Sleep for a given amount of time and update individual API endpoints for each vehicle
485
+ #print('')
486
+ #print(f"Sleeping for {INTERVAL} seconds")
487
+ #await asyncio.sleep(INTERVAL)
488
+
489
+ for vehicle in connection.vehicles:
490
+ """print('')
491
+ print(datetime.now())
492
+ print('')
493
+ print('########################################')
494
+ print('# Update charger data #')
495
+ print(txt.center(40, '#'))
496
+ await vehicle.get_charger()
497
+ print('')
498
+ print('########################################')
499
+ print('# Update climater data #')
500
+ print(txt.center(40, '#'))
501
+ await vehicle.get_climater()
502
+ print('')
503
+ print('########################################')
504
+ print('# Update position data #')
505
+ print(txt.center(40, '#'))
506
+ await vehicle.get_position()
507
+ print('')
508
+ print('########################################')
509
+ print('# Update preheater data #')
510
+ print(txt.center(40, '#'))
511
+ await vehicle.get_preheater()
512
+ print('')
513
+ print('########################################')
514
+ print('# Update realcar data #')
515
+ print(txt.center(40, '#'))
516
+ await vehicle.get_realcardata()
517
+ print('')
518
+ print('########################################')
519
+ print('# Update status data #')
520
+ print(txt.center(40, '#'))
521
+ await vehicle.get_statusreport()
522
+ print('')
523
+ print('########################################')
524
+ print('# Update timer programming #')
525
+ print(txt.center(40, '#'))
526
+ await vehicle.get_timerprogramming()
527
+ print('')
528
+ print('########################################')
529
+ print('# Update trip statistics #')
530
+ print(txt.center(40, '#'))
531
+ await vehicle.get_trip_statistic()
532
+ print('')
533
+ print('Updates complete')
534
+
535
+ print(f"Sleeping for {INTERVAL} seconds")
536
+ await asyncio.sleep(INTERVAL)"""
537
+
538
+ print('########################################')
539
+ print('# Export driving data to csv #')
540
+ print(txt.center(40, '#'))
541
+ exportToCSV(vehicle, credentials.get('csvFileName','./drivingData.csv'), 'short') # possible value: short/cyclic
542
+ print('')
543
+ print('Export of driving data to csv complete')
544
+
545
+ # Examples for using set functions:
546
+
547
+ #await demo_set_charger(vehicle, action = "start") # action = "start" or "stop"
548
+ #await demo_set_charger_current(vehicle, value='reduced') # value = 1-255/Maximum/Reduced (PHEV: 252 for reduced and 254 for max, EV: Maximum/Reduced)
549
+ #await demo_set_charger_target_soc(vehicle, value=70) # value = 1-100
550
+
551
+ #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
552
+ #await demo_set_climatisation_temp(vehicle, temp = 18.0) # temp = integer from 16 to 30
553
+ #await demo_set_battery_climatisation(vehicle, mode=False) # mode = False or True
554
+ #await demo_set_windowheating(vehicle, action = "stop") # action = "start" or "stop"
555
+
556
+ #await demo_set_timer_schedule(vehicle) # arguments id and schedule can be found in the demo function
557
+ #await demo_set_timer_active(vehicle, id=3, action="off") # id = 1, 2, 3, action = "on" or "off".
558
+ #await demo_set_charge_limit(vehicle, 30) # limit = PHEV: 0/10/20/30/40/50, EV: 50/60/70/80/90/100
559
+
560
+ #await demo_set_departure_profile_schedule(vehicle) # arguments id and schedule can be found in the demo function
561
+ #await demo_set_departure_profile_active(vehicle, id=3, action="off") # id = 1, 2, 3, action = "on" or "off".
562
+
563
+ #await demo_set_lock(vehicle,action = "lock",
564
+ # spin = credentials.get('spin','')) # action = "unlock" or "lock". spin = SPIN, needed for both
565
+
566
+ #await vehicle.set_pheater(mode = "heating", spin = "1234") # action = "heating", "ventilation" or "off". spin = SPIN, not needed for off
567
+
568
+ #await demo_set_honkandflash(vehicle, action="flash") # action = "honkandflash" or "flash"
569
+
570
+ #await vehicle.set_refresh() # Takes no arguments, will trigger forced update
571
+
572
+ #print(f"Sleeping for {2*INTERVAL} seconds")
573
+ #await asyncio.sleep(2*INTERVAL)
574
+ #await demo_show_last_honkandflash_info(vehicle) # Returns the info of the last honkandflash_action
575
+
576
+ #await demo_send_destination(vehicle) # arguments can be found in the demo function
577
+
578
+ print('########################################')
579
+ print('# Export all attributes to file #')
580
+ print(txt.center(40, '#'))
581
+ rc= exportAllAttributes(vehicle, ALL_ATTRIBUTES_FILE_NAME_AND_PATH)
582
+ print('')
583
+ if rc:
584
+ print('Export of all attributes successfully completed')
585
+ else:
586
+ print('Export of all attributes failed')
587
+
588
+ if vehicle.firebaseStatus== 1: # firebase messaging activated
589
+ # Do an endless loop to wait and receive firebase messages
590
+ i=0
591
+ while True:
592
+ print(f"Sleeping for {6*INTERVAL} seconds")
593
+ await asyncio.sleep(6*INTERVAL)
594
+ i=i+1
595
+ _LOGGER.debug(f'Round {i}')
596
+
597
+ exit
598
+
599
+ if __name__ == "__main__":
600
+ loop = asyncio.new_event_loop()
601
+ asyncio.set_event_loop(loop)
602
+ loop.run_until_complete(main())
603
+
pycupra/connection.py CHANGED
@@ -15,6 +15,7 @@ import string
15
15
  import secrets
16
16
  import xmltodict
17
17
  from copy import deepcopy
18
+ import importlib.metadata
18
19
 
19
20
  from PIL import Image
20
21
  from io import BytesIO
@@ -26,7 +27,7 @@ from jwt.exceptions import ExpiredSignatureError
26
27
  import aiohttp
27
28
  from bs4 import BeautifulSoup
28
29
  from base64 import b64decode, b64encode, urlsafe_b64decode, urlsafe_b64encode
29
- from .__version__ import __version__ as lib_version
30
+ #from .__version__ import __version__ as lib_version
30
31
  from .utilities import read_config, json_loads
31
32
  from .vehicle import Vehicle
32
33
  from .exceptions import (
@@ -102,6 +103,7 @@ from .const import (
102
103
  )
103
104
 
104
105
  version_info >= (3, 0) or exit('Python 3 required')
106
+ lib_version = importlib.metadata.version("pycupra")
105
107
 
106
108
  _LOGGER = logging.getLogger(__name__)
107
109
  BRAND_CUPRA = 'cupra'