pycupra 0.1.12__tar.gz → 0.1.14__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pycupra-0.1.12 → pycupra-0.1.14}/.gitignore +10 -10
- pycupra-0.1.14/PKG-INFO +63 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/example/PyCupra.py +12 -4
- pycupra-0.1.14/example/PyCupra_ExportDrivingData.py +113 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/connection.py +78 -68
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/dashboard.py +36 -36
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase.py +11 -8
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase_messaging/android_checkin_pb2.pyi +257 -257
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase_messaging/fcmpushclient.py +8 -7
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase_messaging/fcmregister.py +3 -3
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/utilities.py +2 -26
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/vehicle.py +299 -226
- pycupra-0.1.14/pycupra.egg-info/PKG-INFO +63 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra.egg-info/SOURCES.txt +1 -2
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra.egg-info/requires.txt +4 -0
- pycupra-0.1.14/pycupra.egg-info/top_level.txt +3 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra_credentials.json.demo +1 -0
- pycupra-0.1.14/pyproject.toml +27 -0
- pycupra-0.1.12/PKG-INFO +0 -13
- pycupra-0.1.12/pycupra/__version__.py +0 -6
- pycupra-0.1.12/pycupra.egg-info/PKG-INFO +0 -13
- pycupra-0.1.12/pycupra.egg-info/top_level.txt +0 -1
- pycupra-0.1.12/pyproject.toml +0 -16
- pycupra-0.1.12/requirements.txt +0 -6
- {pycupra-0.1.12 → pycupra-0.1.14}/LICENSE +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/README.md +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/__init__.py +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/const.py +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/exceptions.py +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase_messaging/__init__.py +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase_messaging/android_checkin.proto +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase_messaging/android_checkin_pb2.py +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase_messaging/checkin.proto +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase_messaging/checkin_pb2.py +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase_messaging/checkin_pb2.pyi +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase_messaging/const.py +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase_messaging/mcs.proto +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase_messaging/mcs_pb2.py +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase_messaging/mcs_pb2.pyi +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra/firebase_messaging/py.typed +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/pycupra.egg-info/dependency_links.txt +0 -0
- {pycupra-0.1.12 → pycupra-0.1.14}/setup.cfg +0 -0
@@ -1,10 +1,10 @@
|
|
1
|
-
|
2
|
-
.eggs/
|
3
|
-
*.egg-info/
|
4
|
-
build/
|
5
|
-
dist/
|
6
|
-
__pycache__/
|
7
|
-
*.json
|
8
|
-
*.txt
|
9
|
-
*.log
|
10
|
-
|
1
|
+
|
2
|
+
.eggs/
|
3
|
+
*.egg-info/
|
4
|
+
build/
|
5
|
+
dist/
|
6
|
+
__pycache__/
|
7
|
+
*.json
|
8
|
+
*.txt
|
9
|
+
*.log
|
10
|
+
*.png
|
pycupra-0.1.14/PKG-INFO
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: pycupra
|
3
|
+
Version: 0.1.14
|
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.12
|
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
|
+
Requires-Dist: pillow
|
17
|
+
Requires-Dist: protobuf
|
18
|
+
Requires-Dist: http-ece
|
19
|
+
Requires-Dist: requests-oauthlib
|
20
|
+
Dynamic: license-file
|
21
|
+
|
22
|
+
# PyCupra
|
23
|
+
|
24
|
+
A library to read and send vehicle data via Cupra/Seat portal using the same API calls as the MyCupra/MySeat mobile app.
|
25
|
+
|
26
|
+
Fork of https://github.com/Farfar/seatconnect which in turn is a fork of:
|
27
|
+
Fork of https://github.com/lendy007/skodaconnect which in turn is a fork of:
|
28
|
+
https://github.com/robinostlund/volkswagencarnet
|
29
|
+
|
30
|
+
## Information
|
31
|
+
|
32
|
+
Retrieve statistics about your Cupra/Seat from the Cupra/Seat Connect online service
|
33
|
+
|
34
|
+
No licence, public domain, no guarantees, feel free to use for anything. Please contribute improvements/bugfixes etc.
|
35
|
+
|
36
|
+
## Breaking changes
|
37
|
+
|
38
|
+
- The method vehicle.update(updateType) supports 3 different update types:
|
39
|
+
- 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.
|
40
|
+
- updateType=1: Full update (nearly all get-methods are called. The model images and the capabilitites are refreshed only every 2 hours.)
|
41
|
+
- updateType=2: Like updateType=0, but ignoring the nightly update reduction
|
42
|
+
|
43
|
+
- 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.
|
44
|
+
|
45
|
+
- 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 ....
|
46
|
+
|
47
|
+
## Thanks to
|
48
|
+
|
49
|
+
- [RobinostLund](https://github.com/robinostlund/volkswagencarnet) for initial project for Volkswagen Carnet I was able to fork
|
50
|
+
- [Farfar](https://github.com/Farfar) for modifications related to electric engines
|
51
|
+
- [tanelvakker](https://github.com/tanelvakker) for modifications related to correct SPIN handling for various actions and using correct URLs also for MY2021
|
52
|
+
- [sdb9696](https://github.com/sdb9696) for the firebase-messaging package that is used in PyCupra with only minor modifications
|
53
|
+
|
54
|
+
### Example
|
55
|
+
|
56
|
+
For an extensive example, please use the code found in example/PyCupra.py.
|
57
|
+
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.
|
58
|
+
Method get_vehicles will fetch vehicle basic information and create Vehicle class objects for all associated vehicles in account.
|
59
|
+
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.
|
60
|
+
|
61
|
+
The file *pycupra_credentials.json.demo* explains the data structure of the credentials file.
|
62
|
+
|
63
|
+
|
@@ -10,9 +10,11 @@ import pandas as pd
|
|
10
10
|
from aiohttp import ClientSession
|
11
11
|
from datetime import datetime
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
currentframe = inspect.currentframe()
|
14
|
+
if currentframe != None:
|
15
|
+
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(currentframe)))
|
16
|
+
parentdir = os.path.dirname(currentdir)
|
17
|
+
sys.path.insert(0, parentdir)
|
16
18
|
|
17
19
|
try:
|
18
20
|
from pycupra import Connection
|
@@ -383,6 +385,12 @@ async def main():
|
|
383
385
|
if credentials==None or credentials.get('username','')=='' or (credentials.get('password','')==''):
|
384
386
|
_LOGGER.warning('Can not use the credentials read from the credentials file.')
|
385
387
|
raise
|
388
|
+
if credentials.get('brand','')!='':
|
389
|
+
BRAND = credentials.get('brand','')
|
390
|
+
print('Read brand from the credentials file.')
|
391
|
+
else:
|
392
|
+
print('No brand found in the credentials file. Using the default value.')
|
393
|
+
print(f'Now working with brand={BRAND}')
|
386
394
|
async with ClientSession(headers={'Connection': 'keep-alive'}) as session:
|
387
395
|
print('')
|
388
396
|
print('######################################################')
|
@@ -594,7 +602,7 @@ async def main():
|
|
594
602
|
i=i+1
|
595
603
|
_LOGGER.debug(f'Round {i}')
|
596
604
|
|
597
|
-
exit
|
605
|
+
sys.exit(1)
|
598
606
|
|
599
607
|
if __name__ == "__main__":
|
600
608
|
loop = asyncio.new_event_loop()
|
@@ -0,0 +1,113 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
""" Sample program to export the trip statistics as csv file"""
|
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.WARN)
|
24
|
+
_LOGGER = logging.getLogger(__name__)
|
25
|
+
BRAND = 'cupra' # or 'seat' (Default value if no brand is provided via credentials file)
|
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
|
+
|
32
|
+
def readCredentialsFile():
|
33
|
+
try:
|
34
|
+
with open(CREDENTIALS_FILE_NAME_AND_PATH, "r") as f:
|
35
|
+
credentialsString=f.read()
|
36
|
+
credentials=json.loads(credentialsString)
|
37
|
+
return credentials
|
38
|
+
except:
|
39
|
+
_LOGGER.info('readCredentialsFile not successful. Perhaps no credentials file present.')
|
40
|
+
return None
|
41
|
+
|
42
|
+
def exportToCSV(vehicle, csvFileName, dataType='short'):
|
43
|
+
df= pd.DataFrame(vehicle._states['tripstatistics'][dataType])
|
44
|
+
_LOGGER.debug('Exporting trip data to csv')
|
45
|
+
df.to_csv(csvFileName)
|
46
|
+
return True
|
47
|
+
|
48
|
+
async def main():
|
49
|
+
"""Main method."""
|
50
|
+
print('')
|
51
|
+
print('######################################################')
|
52
|
+
print('# Reading credentials file #')
|
53
|
+
print('######################################################')
|
54
|
+
credentials= readCredentialsFile()
|
55
|
+
if credentials==None or credentials.get('username','')=='' or (credentials.get('password','')==''):
|
56
|
+
_LOGGER.warning('Can not use the credentials read from the credentials file.')
|
57
|
+
raise
|
58
|
+
if credentials.get('brand','')!='':
|
59
|
+
BRAND = credentials.get('brand','')
|
60
|
+
print('Read brand from the credentials file.')
|
61
|
+
else:
|
62
|
+
print('No brand found in the credentials file. Using the default value.')
|
63
|
+
print(f'Now working with brand={BRAND}')
|
64
|
+
async with ClientSession(headers={'Connection': 'keep-alive'}) as session:
|
65
|
+
print('')
|
66
|
+
print('######################################################')
|
67
|
+
print('# Logging on to ola.prod.code.seat.cloud.vwgroup.com #')
|
68
|
+
print('######################################################')
|
69
|
+
print(f"Initiating new session to Cupra/Seat Cloud with {credentials.get('username')} as username")
|
70
|
+
connection = Connection(session, BRAND, credentials.get('username'), credentials.get('password'), PRINTRESPONSE, nightlyUpdateReduction=False, anonymise=True, tripStatisticsStartDate='1970-01-01')
|
71
|
+
print("Attempting to login to the Seat Cloud service")
|
72
|
+
if await connection.doLogin(tokenFile=TOKEN_FILE_NAME_AND_PATH, apiKey=credentials.get('apiKey',None)):
|
73
|
+
print('Login or token refresh success!')
|
74
|
+
print(datetime.now())
|
75
|
+
print('Fetching user information for account.')
|
76
|
+
await connection.get_userData()
|
77
|
+
print(f"\tName: {connection._userData.get('name','')}")
|
78
|
+
print(f"\tNickname: {connection._userData.get('nickname','')}")
|
79
|
+
print(f"\tEmail: {connection._userData.get('email','')}")
|
80
|
+
print(f"\tPicture: {connection._userData.get('picture','')}")
|
81
|
+
print("")
|
82
|
+
print('Fetching vehicles associated with account.')
|
83
|
+
await connection.get_vehicles()
|
84
|
+
|
85
|
+
print('')
|
86
|
+
print('########################################')
|
87
|
+
print('# Vehicles discovered #')
|
88
|
+
print('########################################')
|
89
|
+
for vehicle in connection.vehicles:
|
90
|
+
print(f"\tVIN: {vehicle.vin}")
|
91
|
+
print(f"\tModel: {vehicle.model}")
|
92
|
+
print(f"\tManufactured: {vehicle.model_year}")
|
93
|
+
print(f"\tConnect service deactivated: {vehicle.deactivated}")
|
94
|
+
print("")
|
95
|
+
if vehicle.is_nickname_supported: print(f"\tNickname: {vehicle.nickname}")
|
96
|
+
else:
|
97
|
+
return False
|
98
|
+
|
99
|
+
for vehicle in connection.vehicles:
|
100
|
+
txt = vehicle.vin
|
101
|
+
print('########################################')
|
102
|
+
print('# Export driving data to csv #')
|
103
|
+
print(txt.center(40, '#'))
|
104
|
+
exportToCSV(vehicle, credentials.get('csvFileName','./drivingData.csv'), 'short') # possible value: short/cyclic
|
105
|
+
print('')
|
106
|
+
print('Export of driving data to csv complete')
|
107
|
+
sys.exit(1)
|
108
|
+
|
109
|
+
if __name__ == "__main__":
|
110
|
+
loop = asyncio.new_event_loop()
|
111
|
+
asyncio.set_event_loop(loop)
|
112
|
+
loop.run_until_complete(main())
|
113
|
+
|