pycupra 0.1.12__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 +603 -0
- pycupra/connection.py +3 -1
- pycupra/firebase_messaging/android_checkin_pb2.pyi +257 -257
- pycupra-0.1.13.dist-info/METADATA +59 -0
- {pycupra-0.1.12.dist-info → pycupra-0.1.13.dist-info}/RECORD +8 -8
- {pycupra-0.1.12.dist-info → pycupra-0.1.13.dist-info}/top_level.txt +1 -0
- pycupra/__version__.py +0 -6
- pycupra-0.1.12.dist-info/METADATA +0 -13
- {pycupra-0.1.12.dist-info → pycupra-0.1.13.dist-info}/WHEEL +0 -0
- {pycupra-0.1.12.dist-info → pycupra-0.1.13.dist-info}/licenses/LICENSE +0 -0
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'
|
@@ -1,257 +1,257 @@
|
|
1
|
-
"""
|
2
|
-
@generated by mypy-protobuf. Do not edit manually!
|
3
|
-
isort:skip_file
|
4
|
-
Copyright 2014 The Chromium Authors. All rights reserved.
|
5
|
-
Use of this source code is governed by a BSD-style license that can be
|
6
|
-
found in the LICENSE file.
|
7
|
-
|
8
|
-
Logging information for Android "checkin" events (automatic, periodic
|
9
|
-
requests made by Android devices to the server).
|
10
|
-
"""
|
11
|
-
|
12
|
-
import builtins
|
13
|
-
import google.protobuf.descriptor
|
14
|
-
import google.protobuf.internal.enum_type_wrapper
|
15
|
-
import google.protobuf.message
|
16
|
-
import sys
|
17
|
-
import typing
|
18
|
-
|
19
|
-
if sys.version_info >= (3, 10):
|
20
|
-
import typing as typing_extensions
|
21
|
-
else:
|
22
|
-
import typing_extensions
|
23
|
-
|
24
|
-
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
25
|
-
|
26
|
-
class _DeviceType:
|
27
|
-
ValueType = typing.NewType("ValueType", builtins.int)
|
28
|
-
V: typing_extensions.TypeAlias = ValueType
|
29
|
-
|
30
|
-
class _DeviceTypeEnumTypeWrapper(
|
31
|
-
google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_DeviceType.ValueType],
|
32
|
-
builtins.type,
|
33
|
-
):
|
34
|
-
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
35
|
-
DEVICE_ANDROID_OS: _DeviceType.ValueType # 1
|
36
|
-
"""Android Device"""
|
37
|
-
DEVICE_IOS_OS: _DeviceType.ValueType # 2
|
38
|
-
"""Apple IOS device"""
|
39
|
-
DEVICE_CHROME_BROWSER: _DeviceType.ValueType # 3
|
40
|
-
"""Chrome browser - Not Chrome OS. No hardware records."""
|
41
|
-
DEVICE_CHROME_OS: _DeviceType.ValueType # 4
|
42
|
-
"""Chrome OS"""
|
43
|
-
|
44
|
-
class DeviceType(_DeviceType, metaclass=_DeviceTypeEnumTypeWrapper):
|
45
|
-
"""enum values correspond to the type of device.
|
46
|
-
Used in the AndroidCheckinProto and Device proto.
|
47
|
-
"""
|
48
|
-
|
49
|
-
DEVICE_ANDROID_OS: DeviceType.ValueType # 1
|
50
|
-
"""Android Device"""
|
51
|
-
DEVICE_IOS_OS: DeviceType.ValueType # 2
|
52
|
-
"""Apple IOS device"""
|
53
|
-
DEVICE_CHROME_BROWSER: DeviceType.ValueType # 3
|
54
|
-
"""Chrome browser - Not Chrome OS. No hardware records."""
|
55
|
-
DEVICE_CHROME_OS: DeviceType.ValueType # 4
|
56
|
-
"""Chrome OS"""
|
57
|
-
global___DeviceType = DeviceType
|
58
|
-
|
59
|
-
@typing_extensions.final
|
60
|
-
class ChromeBuildProto(google.protobuf.message.Message):
|
61
|
-
"""Build characteristics unique to the Chrome browser, and Chrome OS"""
|
62
|
-
|
63
|
-
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
64
|
-
|
65
|
-
class _Platform:
|
66
|
-
ValueType = typing.NewType("ValueType", builtins.int)
|
67
|
-
V: typing_extensions.TypeAlias = ValueType
|
68
|
-
|
69
|
-
class _PlatformEnumTypeWrapper(
|
70
|
-
google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[
|
71
|
-
ChromeBuildProto._Platform.ValueType
|
72
|
-
],
|
73
|
-
builtins.type,
|
74
|
-
):
|
75
|
-
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
76
|
-
PLATFORM_WIN: ChromeBuildProto._Platform.ValueType # 1
|
77
|
-
PLATFORM_MAC: ChromeBuildProto._Platform.ValueType # 2
|
78
|
-
PLATFORM_LINUX: ChromeBuildProto._Platform.ValueType # 3
|
79
|
-
PLATFORM_CROS: ChromeBuildProto._Platform.ValueType # 4
|
80
|
-
PLATFORM_IOS: ChromeBuildProto._Platform.ValueType # 5
|
81
|
-
PLATFORM_ANDROID: ChromeBuildProto._Platform.ValueType # 6
|
82
|
-
"""Just a placeholder. Likely don't need it due to the presence of the
|
83
|
-
Android GCM on phone/tablet devices.
|
84
|
-
"""
|
85
|
-
|
86
|
-
class Platform(_Platform, metaclass=_PlatformEnumTypeWrapper): ...
|
87
|
-
PLATFORM_WIN: ChromeBuildProto.Platform.ValueType # 1
|
88
|
-
PLATFORM_MAC: ChromeBuildProto.Platform.ValueType # 2
|
89
|
-
PLATFORM_LINUX: ChromeBuildProto.Platform.ValueType # 3
|
90
|
-
PLATFORM_CROS: ChromeBuildProto.Platform.ValueType # 4
|
91
|
-
PLATFORM_IOS: ChromeBuildProto.Platform.ValueType # 5
|
92
|
-
PLATFORM_ANDROID: ChromeBuildProto.Platform.ValueType # 6
|
93
|
-
"""Just a placeholder. Likely don't need it due to the presence of the
|
94
|
-
Android GCM on phone/tablet devices.
|
95
|
-
"""
|
96
|
-
|
97
|
-
class _Channel:
|
98
|
-
ValueType = typing.NewType("ValueType", builtins.int)
|
99
|
-
V: typing_extensions.TypeAlias = ValueType
|
100
|
-
|
101
|
-
class _ChannelEnumTypeWrapper(
|
102
|
-
google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[
|
103
|
-
ChromeBuildProto._Channel.ValueType
|
104
|
-
],
|
105
|
-
builtins.type,
|
106
|
-
):
|
107
|
-
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
108
|
-
CHANNEL_STABLE: ChromeBuildProto._Channel.ValueType # 1
|
109
|
-
CHANNEL_BETA: ChromeBuildProto._Channel.ValueType # 2
|
110
|
-
CHANNEL_DEV: ChromeBuildProto._Channel.ValueType # 3
|
111
|
-
CHANNEL_CANARY: ChromeBuildProto._Channel.ValueType # 4
|
112
|
-
CHANNEL_UNKNOWN: ChromeBuildProto._Channel.ValueType # 5
|
113
|
-
"""for tip of tree or custom builds"""
|
114
|
-
|
115
|
-
class Channel(_Channel, metaclass=_ChannelEnumTypeWrapper): ...
|
116
|
-
CHANNEL_STABLE: ChromeBuildProto.Channel.ValueType # 1
|
117
|
-
CHANNEL_BETA: ChromeBuildProto.Channel.ValueType # 2
|
118
|
-
CHANNEL_DEV: ChromeBuildProto.Channel.ValueType # 3
|
119
|
-
CHANNEL_CANARY: ChromeBuildProto.Channel.ValueType # 4
|
120
|
-
CHANNEL_UNKNOWN: ChromeBuildProto.Channel.ValueType # 5
|
121
|
-
"""for tip of tree or custom builds"""
|
122
|
-
|
123
|
-
PLATFORM_FIELD_NUMBER: builtins.int
|
124
|
-
CHROME_VERSION_FIELD_NUMBER: builtins.int
|
125
|
-
CHANNEL_FIELD_NUMBER: builtins.int
|
126
|
-
platform: global___ChromeBuildProto.Platform.ValueType
|
127
|
-
"""The platform of the device."""
|
128
|
-
chrome_version: builtins.str
|
129
|
-
"""The Chrome instance's version."""
|
130
|
-
channel: global___ChromeBuildProto.Channel.ValueType
|
131
|
-
"""The Channel (build type) of Chrome."""
|
132
|
-
def __init__(
|
133
|
-
self,
|
134
|
-
*,
|
135
|
-
platform: global___ChromeBuildProto.Platform.ValueType | None = ...,
|
136
|
-
chrome_version: builtins.str | None = ...,
|
137
|
-
channel: global___ChromeBuildProto.Channel.ValueType | None = ...,
|
138
|
-
) -> None: ...
|
139
|
-
def HasField(
|
140
|
-
self,
|
141
|
-
field_name: typing_extensions.Literal[
|
142
|
-
"channel",
|
143
|
-
b"channel",
|
144
|
-
"chrome_version",
|
145
|
-
b"chrome_version",
|
146
|
-
"platform",
|
147
|
-
b"platform",
|
148
|
-
],
|
149
|
-
) -> builtins.bool: ...
|
150
|
-
def ClearField(
|
151
|
-
self,
|
152
|
-
field_name: typing_extensions.Literal[
|
153
|
-
"channel",
|
154
|
-
b"channel",
|
155
|
-
"chrome_version",
|
156
|
-
b"chrome_version",
|
157
|
-
"platform",
|
158
|
-
b"platform",
|
159
|
-
],
|
160
|
-
) -> None: ...
|
161
|
-
|
162
|
-
global___ChromeBuildProto = ChromeBuildProto
|
163
|
-
|
164
|
-
@typing_extensions.final
|
165
|
-
class AndroidCheckinProto(google.protobuf.message.Message):
|
166
|
-
"""Information sent by the device in a "checkin" request."""
|
167
|
-
|
168
|
-
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
169
|
-
|
170
|
-
LAST_CHECKIN_MSEC_FIELD_NUMBER: builtins.int
|
171
|
-
CELL_OPERATOR_FIELD_NUMBER: builtins.int
|
172
|
-
SIM_OPERATOR_FIELD_NUMBER: builtins.int
|
173
|
-
ROAMING_FIELD_NUMBER: builtins.int
|
174
|
-
USER_NUMBER_FIELD_NUMBER: builtins.int
|
175
|
-
TYPE_FIELD_NUMBER: builtins.int
|
176
|
-
CHROME_BUILD_FIELD_NUMBER: builtins.int
|
177
|
-
last_checkin_msec: builtins.int
|
178
|
-
"""Miliseconds since the Unix epoch of the device's last successful checkin."""
|
179
|
-
cell_operator: builtins.str
|
180
|
-
"""The current MCC+MNC of the mobile device's current cell."""
|
181
|
-
sim_operator: builtins.str
|
182
|
-
"""The MCC+MNC of the SIM card (different from operator if the
|
183
|
-
device is roaming, for instance).
|
184
|
-
"""
|
185
|
-
roaming: builtins.str
|
186
|
-
"""The device's current roaming state (reported starting in eclair builds).
|
187
|
-
Currently one of "{,not}mobile-{,not}roaming", if it is present at all.
|
188
|
-
"""
|
189
|
-
user_number: builtins.int
|
190
|
-
"""For devices supporting multiple user profiles (which may be
|
191
|
-
supported starting in jellybean), the ordinal number of the
|
192
|
-
profile that is checking in. This is 0 for the primary profile
|
193
|
-
(which can't be changed without wiping the device), and 1,2,3,...
|
194
|
-
for additional profiles (which can be added and deleted freely).
|
195
|
-
"""
|
196
|
-
type: global___DeviceType.ValueType
|
197
|
-
"""Class of device. Indicates the type of build proto
|
198
|
-
(IosBuildProto/ChromeBuildProto/AndroidBuildProto)
|
199
|
-
That is included in this proto
|
200
|
-
"""
|
201
|
-
@property
|
202
|
-
def chrome_build(self) -> global___ChromeBuildProto:
|
203
|
-
"""For devices running MCS on Chrome, build-specific characteristics
|
204
|
-
of the browser. There are no hardware aspects (except for ChromeOS).
|
205
|
-
This will only be populated for Chrome builds/ChromeOS devices
|
206
|
-
"""
|
207
|
-
def __init__(
|
208
|
-
self,
|
209
|
-
*,
|
210
|
-
last_checkin_msec: builtins.int | None = ...,
|
211
|
-
cell_operator: builtins.str | None = ...,
|
212
|
-
sim_operator: builtins.str | None = ...,
|
213
|
-
roaming: builtins.str | None = ...,
|
214
|
-
user_number: builtins.int | None = ...,
|
215
|
-
type: global___DeviceType.ValueType | None = ...,
|
216
|
-
chrome_build: global___ChromeBuildProto | None = ...,
|
217
|
-
) -> None: ...
|
218
|
-
def HasField(
|
219
|
-
self,
|
220
|
-
field_name: typing_extensions.Literal[
|
221
|
-
"cell_operator",
|
222
|
-
b"cell_operator",
|
223
|
-
"chrome_build",
|
224
|
-
b"chrome_build",
|
225
|
-
"last_checkin_msec",
|
226
|
-
b"last_checkin_msec",
|
227
|
-
"roaming",
|
228
|
-
b"roaming",
|
229
|
-
"sim_operator",
|
230
|
-
b"sim_operator",
|
231
|
-
"type",
|
232
|
-
b"type",
|
233
|
-
"user_number",
|
234
|
-
b"user_number",
|
235
|
-
],
|
236
|
-
) -> builtins.bool: ...
|
237
|
-
def ClearField(
|
238
|
-
self,
|
239
|
-
field_name: typing_extensions.Literal[
|
240
|
-
"cell_operator",
|
241
|
-
b"cell_operator",
|
242
|
-
"chrome_build",
|
243
|
-
b"chrome_build",
|
244
|
-
"last_checkin_msec",
|
245
|
-
b"last_checkin_msec",
|
246
|
-
"roaming",
|
247
|
-
b"roaming",
|
248
|
-
"sim_operator",
|
249
|
-
b"sim_operator",
|
250
|
-
"type",
|
251
|
-
b"type",
|
252
|
-
"user_number",
|
253
|
-
b"user_number",
|
254
|
-
],
|
255
|
-
) -> None: ...
|
256
|
-
|
257
|
-
global___AndroidCheckinProto = AndroidCheckinProto
|
1
|
+
"""
|
2
|
+
@generated by mypy-protobuf. Do not edit manually!
|
3
|
+
isort:skip_file
|
4
|
+
Copyright 2014 The Chromium Authors. All rights reserved.
|
5
|
+
Use of this source code is governed by a BSD-style license that can be
|
6
|
+
found in the LICENSE file.
|
7
|
+
|
8
|
+
Logging information for Android "checkin" events (automatic, periodic
|
9
|
+
requests made by Android devices to the server).
|
10
|
+
"""
|
11
|
+
|
12
|
+
import builtins
|
13
|
+
import google.protobuf.descriptor
|
14
|
+
import google.protobuf.internal.enum_type_wrapper
|
15
|
+
import google.protobuf.message
|
16
|
+
import sys
|
17
|
+
import typing
|
18
|
+
|
19
|
+
if sys.version_info >= (3, 10):
|
20
|
+
import typing as typing_extensions
|
21
|
+
else:
|
22
|
+
import typing_extensions
|
23
|
+
|
24
|
+
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
25
|
+
|
26
|
+
class _DeviceType:
|
27
|
+
ValueType = typing.NewType("ValueType", builtins.int)
|
28
|
+
V: typing_extensions.TypeAlias = ValueType
|
29
|
+
|
30
|
+
class _DeviceTypeEnumTypeWrapper(
|
31
|
+
google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_DeviceType.ValueType],
|
32
|
+
builtins.type,
|
33
|
+
):
|
34
|
+
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
35
|
+
DEVICE_ANDROID_OS: _DeviceType.ValueType # 1
|
36
|
+
"""Android Device"""
|
37
|
+
DEVICE_IOS_OS: _DeviceType.ValueType # 2
|
38
|
+
"""Apple IOS device"""
|
39
|
+
DEVICE_CHROME_BROWSER: _DeviceType.ValueType # 3
|
40
|
+
"""Chrome browser - Not Chrome OS. No hardware records."""
|
41
|
+
DEVICE_CHROME_OS: _DeviceType.ValueType # 4
|
42
|
+
"""Chrome OS"""
|
43
|
+
|
44
|
+
class DeviceType(_DeviceType, metaclass=_DeviceTypeEnumTypeWrapper):
|
45
|
+
"""enum values correspond to the type of device.
|
46
|
+
Used in the AndroidCheckinProto and Device proto.
|
47
|
+
"""
|
48
|
+
|
49
|
+
DEVICE_ANDROID_OS: DeviceType.ValueType # 1
|
50
|
+
"""Android Device"""
|
51
|
+
DEVICE_IOS_OS: DeviceType.ValueType # 2
|
52
|
+
"""Apple IOS device"""
|
53
|
+
DEVICE_CHROME_BROWSER: DeviceType.ValueType # 3
|
54
|
+
"""Chrome browser - Not Chrome OS. No hardware records."""
|
55
|
+
DEVICE_CHROME_OS: DeviceType.ValueType # 4
|
56
|
+
"""Chrome OS"""
|
57
|
+
global___DeviceType = DeviceType
|
58
|
+
|
59
|
+
@typing_extensions.final
|
60
|
+
class ChromeBuildProto(google.protobuf.message.Message):
|
61
|
+
"""Build characteristics unique to the Chrome browser, and Chrome OS"""
|
62
|
+
|
63
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
64
|
+
|
65
|
+
class _Platform:
|
66
|
+
ValueType = typing.NewType("ValueType", builtins.int)
|
67
|
+
V: typing_extensions.TypeAlias = ValueType
|
68
|
+
|
69
|
+
class _PlatformEnumTypeWrapper(
|
70
|
+
google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[
|
71
|
+
ChromeBuildProto._Platform.ValueType
|
72
|
+
],
|
73
|
+
builtins.type,
|
74
|
+
):
|
75
|
+
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
76
|
+
PLATFORM_WIN: ChromeBuildProto._Platform.ValueType # 1
|
77
|
+
PLATFORM_MAC: ChromeBuildProto._Platform.ValueType # 2
|
78
|
+
PLATFORM_LINUX: ChromeBuildProto._Platform.ValueType # 3
|
79
|
+
PLATFORM_CROS: ChromeBuildProto._Platform.ValueType # 4
|
80
|
+
PLATFORM_IOS: ChromeBuildProto._Platform.ValueType # 5
|
81
|
+
PLATFORM_ANDROID: ChromeBuildProto._Platform.ValueType # 6
|
82
|
+
"""Just a placeholder. Likely don't need it due to the presence of the
|
83
|
+
Android GCM on phone/tablet devices.
|
84
|
+
"""
|
85
|
+
|
86
|
+
class Platform(_Platform, metaclass=_PlatformEnumTypeWrapper): ...
|
87
|
+
PLATFORM_WIN: ChromeBuildProto.Platform.ValueType # 1
|
88
|
+
PLATFORM_MAC: ChromeBuildProto.Platform.ValueType # 2
|
89
|
+
PLATFORM_LINUX: ChromeBuildProto.Platform.ValueType # 3
|
90
|
+
PLATFORM_CROS: ChromeBuildProto.Platform.ValueType # 4
|
91
|
+
PLATFORM_IOS: ChromeBuildProto.Platform.ValueType # 5
|
92
|
+
PLATFORM_ANDROID: ChromeBuildProto.Platform.ValueType # 6
|
93
|
+
"""Just a placeholder. Likely don't need it due to the presence of the
|
94
|
+
Android GCM on phone/tablet devices.
|
95
|
+
"""
|
96
|
+
|
97
|
+
class _Channel:
|
98
|
+
ValueType = typing.NewType("ValueType", builtins.int)
|
99
|
+
V: typing_extensions.TypeAlias = ValueType
|
100
|
+
|
101
|
+
class _ChannelEnumTypeWrapper(
|
102
|
+
google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[
|
103
|
+
ChromeBuildProto._Channel.ValueType
|
104
|
+
],
|
105
|
+
builtins.type,
|
106
|
+
):
|
107
|
+
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
108
|
+
CHANNEL_STABLE: ChromeBuildProto._Channel.ValueType # 1
|
109
|
+
CHANNEL_BETA: ChromeBuildProto._Channel.ValueType # 2
|
110
|
+
CHANNEL_DEV: ChromeBuildProto._Channel.ValueType # 3
|
111
|
+
CHANNEL_CANARY: ChromeBuildProto._Channel.ValueType # 4
|
112
|
+
CHANNEL_UNKNOWN: ChromeBuildProto._Channel.ValueType # 5
|
113
|
+
"""for tip of tree or custom builds"""
|
114
|
+
|
115
|
+
class Channel(_Channel, metaclass=_ChannelEnumTypeWrapper): ...
|
116
|
+
CHANNEL_STABLE: ChromeBuildProto.Channel.ValueType # 1
|
117
|
+
CHANNEL_BETA: ChromeBuildProto.Channel.ValueType # 2
|
118
|
+
CHANNEL_DEV: ChromeBuildProto.Channel.ValueType # 3
|
119
|
+
CHANNEL_CANARY: ChromeBuildProto.Channel.ValueType # 4
|
120
|
+
CHANNEL_UNKNOWN: ChromeBuildProto.Channel.ValueType # 5
|
121
|
+
"""for tip of tree or custom builds"""
|
122
|
+
|
123
|
+
PLATFORM_FIELD_NUMBER: builtins.int
|
124
|
+
CHROME_VERSION_FIELD_NUMBER: builtins.int
|
125
|
+
CHANNEL_FIELD_NUMBER: builtins.int
|
126
|
+
platform: global___ChromeBuildProto.Platform.ValueType
|
127
|
+
"""The platform of the device."""
|
128
|
+
chrome_version: builtins.str
|
129
|
+
"""The Chrome instance's version."""
|
130
|
+
channel: global___ChromeBuildProto.Channel.ValueType
|
131
|
+
"""The Channel (build type) of Chrome."""
|
132
|
+
def __init__(
|
133
|
+
self,
|
134
|
+
*,
|
135
|
+
platform: global___ChromeBuildProto.Platform.ValueType | None = ...,
|
136
|
+
chrome_version: builtins.str | None = ...,
|
137
|
+
channel: global___ChromeBuildProto.Channel.ValueType | None = ...,
|
138
|
+
) -> None: ...
|
139
|
+
def HasField(
|
140
|
+
self,
|
141
|
+
field_name: typing_extensions.Literal[
|
142
|
+
"channel",
|
143
|
+
b"channel",
|
144
|
+
"chrome_version",
|
145
|
+
b"chrome_version",
|
146
|
+
"platform",
|
147
|
+
b"platform",
|
148
|
+
],
|
149
|
+
) -> builtins.bool: ...
|
150
|
+
def ClearField(
|
151
|
+
self,
|
152
|
+
field_name: typing_extensions.Literal[
|
153
|
+
"channel",
|
154
|
+
b"channel",
|
155
|
+
"chrome_version",
|
156
|
+
b"chrome_version",
|
157
|
+
"platform",
|
158
|
+
b"platform",
|
159
|
+
],
|
160
|
+
) -> None: ...
|
161
|
+
|
162
|
+
global___ChromeBuildProto = ChromeBuildProto
|
163
|
+
|
164
|
+
@typing_extensions.final
|
165
|
+
class AndroidCheckinProto(google.protobuf.message.Message):
|
166
|
+
"""Information sent by the device in a "checkin" request."""
|
167
|
+
|
168
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
169
|
+
|
170
|
+
LAST_CHECKIN_MSEC_FIELD_NUMBER: builtins.int
|
171
|
+
CELL_OPERATOR_FIELD_NUMBER: builtins.int
|
172
|
+
SIM_OPERATOR_FIELD_NUMBER: builtins.int
|
173
|
+
ROAMING_FIELD_NUMBER: builtins.int
|
174
|
+
USER_NUMBER_FIELD_NUMBER: builtins.int
|
175
|
+
TYPE_FIELD_NUMBER: builtins.int
|
176
|
+
CHROME_BUILD_FIELD_NUMBER: builtins.int
|
177
|
+
last_checkin_msec: builtins.int
|
178
|
+
"""Miliseconds since the Unix epoch of the device's last successful checkin."""
|
179
|
+
cell_operator: builtins.str
|
180
|
+
"""The current MCC+MNC of the mobile device's current cell."""
|
181
|
+
sim_operator: builtins.str
|
182
|
+
"""The MCC+MNC of the SIM card (different from operator if the
|
183
|
+
device is roaming, for instance).
|
184
|
+
"""
|
185
|
+
roaming: builtins.str
|
186
|
+
"""The device's current roaming state (reported starting in eclair builds).
|
187
|
+
Currently one of "{,not}mobile-{,not}roaming", if it is present at all.
|
188
|
+
"""
|
189
|
+
user_number: builtins.int
|
190
|
+
"""For devices supporting multiple user profiles (which may be
|
191
|
+
supported starting in jellybean), the ordinal number of the
|
192
|
+
profile that is checking in. This is 0 for the primary profile
|
193
|
+
(which can't be changed without wiping the device), and 1,2,3,...
|
194
|
+
for additional profiles (which can be added and deleted freely).
|
195
|
+
"""
|
196
|
+
type: global___DeviceType.ValueType
|
197
|
+
"""Class of device. Indicates the type of build proto
|
198
|
+
(IosBuildProto/ChromeBuildProto/AndroidBuildProto)
|
199
|
+
That is included in this proto
|
200
|
+
"""
|
201
|
+
@property
|
202
|
+
def chrome_build(self) -> global___ChromeBuildProto:
|
203
|
+
"""For devices running MCS on Chrome, build-specific characteristics
|
204
|
+
of the browser. There are no hardware aspects (except for ChromeOS).
|
205
|
+
This will only be populated for Chrome builds/ChromeOS devices
|
206
|
+
"""
|
207
|
+
def __init__(
|
208
|
+
self,
|
209
|
+
*,
|
210
|
+
last_checkin_msec: builtins.int | None = ...,
|
211
|
+
cell_operator: builtins.str | None = ...,
|
212
|
+
sim_operator: builtins.str | None = ...,
|
213
|
+
roaming: builtins.str | None = ...,
|
214
|
+
user_number: builtins.int | None = ...,
|
215
|
+
type: global___DeviceType.ValueType | None = ...,
|
216
|
+
chrome_build: global___ChromeBuildProto | None = ...,
|
217
|
+
) -> None: ...
|
218
|
+
def HasField(
|
219
|
+
self,
|
220
|
+
field_name: typing_extensions.Literal[
|
221
|
+
"cell_operator",
|
222
|
+
b"cell_operator",
|
223
|
+
"chrome_build",
|
224
|
+
b"chrome_build",
|
225
|
+
"last_checkin_msec",
|
226
|
+
b"last_checkin_msec",
|
227
|
+
"roaming",
|
228
|
+
b"roaming",
|
229
|
+
"sim_operator",
|
230
|
+
b"sim_operator",
|
231
|
+
"type",
|
232
|
+
b"type",
|
233
|
+
"user_number",
|
234
|
+
b"user_number",
|
235
|
+
],
|
236
|
+
) -> builtins.bool: ...
|
237
|
+
def ClearField(
|
238
|
+
self,
|
239
|
+
field_name: typing_extensions.Literal[
|
240
|
+
"cell_operator",
|
241
|
+
b"cell_operator",
|
242
|
+
"chrome_build",
|
243
|
+
b"chrome_build",
|
244
|
+
"last_checkin_msec",
|
245
|
+
b"last_checkin_msec",
|
246
|
+
"roaming",
|
247
|
+
b"roaming",
|
248
|
+
"sim_operator",
|
249
|
+
b"sim_operator",
|
250
|
+
"type",
|
251
|
+
b"type",
|
252
|
+
"user_number",
|
253
|
+
b"user_number",
|
254
|
+
],
|
255
|
+
) -> None: ...
|
256
|
+
|
257
|
+
global___AndroidCheckinProto = AndroidCheckinProto
|
@@ -0,0 +1,59 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: pycupra
|
3
|
+
Version: 0.1.13
|
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
|
+
License-Expression: Apache-2.0
|
6
|
+
Requires-Python: >=3.10
|
7
|
+
Description-Content-Type: text/markdown
|
8
|
+
License-File: LICENSE
|
9
|
+
Requires-Dist: aiohttp
|
10
|
+
Requires-Dist: beautifulsoup4
|
11
|
+
Requires-Dist: cryptography
|
12
|
+
Requires-Dist: lxml
|
13
|
+
Requires-Dist: PyJWT
|
14
|
+
Requires-Dist: xmltodict
|
15
|
+
Requires-Dist: pandas
|
16
|
+
Dynamic: license-file
|
17
|
+
|
18
|
+
# PyCupra
|
19
|
+
|
20
|
+
A library to read and send vehicle data via Cupra/Seat portal using the same API calls as the MyCupra/MySeat mobile app.
|
21
|
+
|
22
|
+
Fork of https://github.com/Farfar/seatconnect which in turn is a fork of:
|
23
|
+
Fork of https://github.com/lendy007/skodaconnect which in turn is a fork of:
|
24
|
+
https://github.com/robinostlund/volkswagencarnet
|
25
|
+
|
26
|
+
## Information
|
27
|
+
|
28
|
+
Retrieve statistics about your Cupra/Seat from the Cupra/Seat Connect online service
|
29
|
+
|
30
|
+
No licence, public domain, no guarantees, feel free to use for anything. Please contribute improvements/bugfixes etc.
|
31
|
+
|
32
|
+
## Breaking changes
|
33
|
+
|
34
|
+
- The method vehicle.update(updateType) supports 3 different update types:
|
35
|
+
- updateType=0: Small update (=only get_basiccardata() and get_statusreport are called). If the last full update is more than 1100 seconds ago, then a full update is performed.
|
36
|
+
- updateType=1: Full update (nearly all get-methods are called. The model images and the capabilitites are refreshed only every 2 hours.)
|
37
|
+
- updateType=2: Like updateType=0, but ignoring the nightly update reduction
|
38
|
+
|
39
|
+
- Nightly update reduction: If nightly reduction is activated and the current time is within the time frame between 22:00 and 05:00, then vehicle.update(0) performs a full update, if the last full update is more than 1700 seconds ago. If that's not the case, vehicle.update(0) does nothing.
|
40
|
+
|
41
|
+
- PyCupra can ask the Seat/Cupra portal to send push notifications to PyCupra if the charging status or the climatisation status has changed or when the API has finished a request like lock or unlock vehicle, start or stop charging or change departure timers or ....
|
42
|
+
|
43
|
+
## Thanks to
|
44
|
+
|
45
|
+
- [RobinostLund](https://github.com/robinostlund/volkswagencarnet) for initial project for Volkswagen Carnet I was able to fork
|
46
|
+
- [Farfar](https://github.com/Farfar) for modifications related to electric engines
|
47
|
+
- [tanelvakker](https://github.com/tanelvakker) for modifications related to correct SPIN handling for various actions and using correct URLs also for MY2021
|
48
|
+
- [sdb9696](https://github.com/sdb9696) for the firebase-messaging package that is used in PyCupra with only minor modifications
|
49
|
+
|
50
|
+
### Example
|
51
|
+
|
52
|
+
For an extensive example, please use the code found in example/PyCupra.py.
|
53
|
+
When logged in the library will automatically create a vehicle object for every car registered to the account. Initially no data is fetched at all. Use the doLogin method and it will signin with the credentials used for the class constructor. After a successful login, the tokens are stored in a json file. Later doLogin calls can use the token file instead of the credentials.
|
54
|
+
Method get_vehicles will fetch vehicle basic information and create Vehicle class objects for all associated vehicles in account.
|
55
|
+
To update all available data use the update_all method of the Connect class. This will call the update function for all registered vehicles, which in turn will fetch data from all available API endpoints.
|
56
|
+
|
57
|
+
The file *pycupra_credentials.json.demo* explains the data structure of the credentials file.
|
58
|
+
|
59
|
+
|
@@ -1,6 +1,6 @@
|
|
1
|
+
example/PyCupra.py,sha256=FMg3974wOAo_lVhpjTu2omXENbyecZ_93divOMBeS6I,26722
|
1
2
|
pycupra/__init__.py,sha256=p0880jPkLqOErX3u3qaLboBLOsEHFpe44axApdaGeqI,231
|
2
|
-
pycupra/
|
3
|
-
pycupra/connection.py,sha256=-wfamvOPP0gDGo5Picd2zx9pHGs90vVqqCxp6SFn6PQ,93278
|
3
|
+
pycupra/connection.py,sha256=blnAdaFcXTYhHkKVP4akbL0FdMrU2SNTnpZe_-j4iuE,93359
|
4
4
|
pycupra/const.py,sha256=5b4uuNUE1AGZHmqQfIZv-76Ad20mJTXXQPGI8TU6FfY,10631
|
5
5
|
pycupra/dashboard.py,sha256=_8qlVr1k_L3_7A7dDc9ugzpGK_Dg6UZgv_Pp-1pPaPE,45337
|
6
6
|
pycupra/exceptions.py,sha256=Nq_F79GP8wjHf5lpvPy9TbSIrRHAJrFMo0T1N9TcgSQ,2917
|
@@ -10,7 +10,7 @@ pycupra/vehicle.py,sha256=svuZ9xabbHa67saOQ1yGJcB8WCxV3zJmp32LFl8f_kQ,164254
|
|
10
10
|
pycupra/firebase_messaging/__init__.py,sha256=oerLHWvEf4qRqu3GxSX6SLY_OYI430ydAiAhKtzyMEM,666
|
11
11
|
pycupra/firebase_messaging/android_checkin.proto,sha256=AW1Ew0iU3NdZpFCocNsG3MxFhEsoRcQH0yMpKhkj0Zg,3109
|
12
12
|
pycupra/firebase_messaging/android_checkin_pb2.py,sha256=-U1oGroFt3KRuGDieae3iTcux6mAfx1TFkE1Q35ul2E,2849
|
13
|
-
pycupra/firebase_messaging/android_checkin_pb2.pyi,sha256=
|
13
|
+
pycupra/firebase_messaging/android_checkin_pb2.pyi,sha256=3ZOH0c4aUujdeCLshpwslQ1DDzdLNzzp7_JV3yOGQYg,9636
|
14
14
|
pycupra/firebase_messaging/checkin.proto,sha256=bTIfw9ffqmNRgOLmJjpCrbJsOPLb5jEcy58EH_CsGaY,6265
|
15
15
|
pycupra/firebase_messaging/checkin_pb2.py,sha256=lFzCIAkYz9NFUpRbVuW-2kM_EaYKVWHeifHS1PV2eHQ,2795
|
16
16
|
pycupra/firebase_messaging/checkin_pb2.pyi,sha256=mHOqbedt5jZDI20jcyFrTMSnQ0f_tq4zkIlHiaSC3xI,14626
|
@@ -21,8 +21,8 @@ pycupra/firebase_messaging/mcs.proto,sha256=HUgnx-KTQiXIER1bJ0swpCt1W0BILru3lxhw
|
|
21
21
|
pycupra/firebase_messaging/mcs_pb2.py,sha256=nwXY7IDgLYPxgpSGs6wyTSyYDdomQsyGqH8R8EgODLg,7733
|
22
22
|
pycupra/firebase_messaging/mcs_pb2.pyi,sha256=HfIhInC3wRg8_caKwUm-V3knE2jTdEQvBy6uXgQ5rHY,33959
|
23
23
|
pycupra/firebase_messaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
|
-
pycupra-0.1.
|
25
|
-
pycupra-0.1.
|
26
|
-
pycupra-0.1.
|
27
|
-
pycupra-0.1.
|
28
|
-
pycupra-0.1.
|
24
|
+
pycupra-0.1.13.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
25
|
+
pycupra-0.1.13.dist-info/METADATA,sha256=c9S1nFVpwzfGMc4hXZwfqHWYQ4xBTbEQ6S-8Q_9KSsA,3577
|
26
|
+
pycupra-0.1.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
27
|
+
pycupra-0.1.13.dist-info/top_level.txt,sha256=utR3J3sG_3PvNyamWDcJnkyxszbts_8tosWSHZ7zfLg,16
|
28
|
+
pycupra-0.1.13.dist-info/RECORD,,
|
pycupra/__version__.py
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.4
|
2
|
-
Name: pycupra
|
3
|
-
Version: 0.1.12
|
4
|
-
Requires-Python: >=3.10
|
5
|
-
License-File: LICENSE
|
6
|
-
Requires-Dist: aiohttp
|
7
|
-
Requires-Dist: beautifulsoup4
|
8
|
-
Requires-Dist: cryptography
|
9
|
-
Requires-Dist: lxml
|
10
|
-
Requires-Dist: PyJWT
|
11
|
-
Requires-Dist: xmltodict
|
12
|
-
Requires-Dist: pandas
|
13
|
-
Dynamic: license-file
|
File without changes
|
File without changes
|