pycupra 0.1.5__py3-none-any.whl → 0.1.6__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.
- pycupra/__version__.py +1 -1
- pycupra/connection.py +1858 -1816
- pycupra/firebase.py +37 -20
- pycupra/utilities.py +116 -116
- pycupra/vehicle.py +57 -10
- {pycupra-0.1.5.dist-info → pycupra-0.1.6.dist-info}/METADATA +1 -1
- {pycupra-0.1.5.dist-info → pycupra-0.1.6.dist-info}/RECORD +10 -10
- {pycupra-0.1.5.dist-info → pycupra-0.1.6.dist-info}/WHEEL +0 -0
- {pycupra-0.1.5.dist-info → pycupra-0.1.6.dist-info}/licenses/LICENSE +0 -0
- {pycupra-0.1.5.dist-info → pycupra-0.1.6.dist-info}/top_level.txt +0 -0
pycupra/firebase.py
CHANGED
@@ -16,29 +16,46 @@ from .const import (
|
|
16
16
|
_LOGGER = logging.getLogger(__name__)
|
17
17
|
|
18
18
|
class Firebase():
|
19
|
+
def __init__(self):
|
20
|
+
self._pushClient = None
|
21
|
+
|
19
22
|
async def firebaseStart(self, onNotificationFunc, firebaseCredentialsFileName, brand='cupra'):
|
20
23
|
""" Starts the firebase cloud messaging receiver """
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
credentials
|
24
|
+
try:
|
25
|
+
loop = asyncio.get_running_loop()
|
26
|
+
credentials = await loop.run_in_executor(None, readFCMCredsFile, firebaseCredentialsFileName)
|
27
|
+
#credentials = readFCMCredsFile(firebaseCredentialsFileName)
|
28
|
+
if credentials == {}:
|
29
|
+
credentials =''
|
26
30
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
fcm_project_id=FCM_PROJECT_ID
|
32
|
+
fcm_app_id=FCM_APP_ID[brand]
|
33
|
+
fcm_api_key=FCM_API_KEY
|
34
|
+
chars = string.ascii_letters + string.digits
|
35
|
+
fcmMessageSenderId = ''.join(secrets.choice(chars) for i in range(16))
|
36
|
+
fcmMessageSenderId= 'fxpWQ_'+fcmMessageSenderId
|
33
37
|
|
38
|
+
fcm_config = FcmRegisterConfig(fcm_project_id, fcm_app_id, fcm_api_key, fcmMessageSenderId)
|
39
|
+
self._pushClient = FcmPushClient(onNotificationFunc, fcm_config, credentials, onFCMCredentialsUpdated)
|
40
|
+
fcm_token = await self._pushClient.checkin_or_register(firebaseCredentialsFileName)
|
41
|
+
_LOGGER.debug(f'Firebase.checkin_or_register() returned a token:{fcm_token}')
|
42
|
+
await self._pushClient.start()
|
43
|
+
await asyncio.sleep(5)
|
44
|
+
return self._pushClient.is_started()
|
45
|
+
except Exception as e:
|
46
|
+
_LOGGER.error('Error in firebaseStart. Error: {e}')
|
47
|
+
return False
|
34
48
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
49
|
+
async def firebaseStop(self):
|
50
|
+
""" Stops the firebase cloud messaging receiver """
|
51
|
+
try:
|
52
|
+
await self._pushClient.stop()
|
53
|
+
#await asyncio.sleep(5)
|
54
|
+
self._pushClient = None
|
55
|
+
return True
|
56
|
+
except Exception as e:
|
57
|
+
_LOGGER.error('Error in firebaseStop. Error: {e}')
|
58
|
+
return False
|
42
59
|
|
43
60
|
def readFCMCredsFile(credsFile):
|
44
61
|
""" Reads the firebase cloud messaging credentials from file"""
|
@@ -52,8 +69,8 @@ def readFCMCredsFile(credsFile):
|
|
52
69
|
else:
|
53
70
|
_LOGGER.debug(f'{credsFile} not found.')
|
54
71
|
return {}
|
55
|
-
except:
|
56
|
-
_LOGGER.warning('readFCMCredsFile() not successful.')
|
72
|
+
except Exception as e:
|
73
|
+
_LOGGER.warning('readFCMCredsFile() not successful. Error: {e}')
|
57
74
|
return ''
|
58
75
|
|
59
76
|
def writeFCMCredsFile(creds, firebaseCredentialsFileName):
|
pycupra/utilities.py
CHANGED
@@ -1,116 +1,116 @@
|
|
1
|
-
from datetime import date, datetime
|
2
|
-
from base64 import b64encode
|
3
|
-
from string import ascii_letters as letters, digits
|
4
|
-
from sys import argv
|
5
|
-
from os import environ as env
|
6
|
-
from os.path import join, dirname, expanduser
|
7
|
-
from itertools import product
|
8
|
-
import json
|
9
|
-
import logging
|
10
|
-
import re
|
11
|
-
|
12
|
-
_LOGGER = logging.getLogger(__name__)
|
13
|
-
|
14
|
-
|
15
|
-
def read_config():
|
16
|
-
"""Read config from file."""
|
17
|
-
for directory, filename in product(
|
18
|
-
[
|
19
|
-
dirname(argv[0]),
|
20
|
-
expanduser("~"),
|
21
|
-
env.get("XDG_CONFIG_HOME", join(expanduser("~"), ".config")),
|
22
|
-
],
|
23
|
-
["seat.conf", ".seat.conf"],
|
24
|
-
):
|
25
|
-
try:
|
26
|
-
config = join(directory, filename)
|
27
|
-
_LOGGER.debug("checking for config file %s", config)
|
28
|
-
with open(config) as config:
|
29
|
-
return dict(
|
30
|
-
x.split(": ")
|
31
|
-
for x in config.read().strip().splitlines()
|
32
|
-
if not x.startswith("#")
|
33
|
-
)
|
34
|
-
except (IOError, OSError):
|
35
|
-
continue
|
36
|
-
return {}
|
37
|
-
|
38
|
-
|
39
|
-
def json_loads(s):
|
40
|
-
return json.loads(s, object_hook=obj_parser)
|
41
|
-
|
42
|
-
|
43
|
-
def obj_parser(obj):
|
44
|
-
"""Parse datetime."""
|
45
|
-
for key, val in obj.items():
|
46
|
-
try:
|
47
|
-
obj[key] = datetime.strptime(val, "%Y-%m-%dT%H:%M:%S%z")
|
48
|
-
except (TypeError, ValueError):
|
49
|
-
pass
|
50
|
-
return obj
|
51
|
-
|
52
|
-
|
53
|
-
def find_path(src, path):
|
54
|
-
"""Simple navigation of a hierarchical dict structure using XPATH-like syntax.
|
55
|
-
|
56
|
-
>>> find_path(dict(a=1), 'a')
|
57
|
-
1
|
58
|
-
|
59
|
-
>>> find_path(dict(a=1), '')
|
60
|
-
{'a': 1}
|
61
|
-
|
62
|
-
>>> find_path(dict(a=None), 'a')
|
63
|
-
|
64
|
-
|
65
|
-
>>> find_path(dict(a=1), 'b')
|
66
|
-
Traceback (most recent call last):
|
67
|
-
...
|
68
|
-
KeyError: 'b'
|
69
|
-
|
70
|
-
>>> find_path(dict(a=dict(b=1)), 'a.b')
|
71
|
-
1
|
72
|
-
|
73
|
-
>>> find_path(dict(a=dict(b=1)), 'a')
|
74
|
-
{'b': 1}
|
75
|
-
|
76
|
-
>>> find_path(dict(a=dict(b=1)), 'a.c')
|
77
|
-
Traceback (most recent call last):
|
78
|
-
...
|
79
|
-
KeyError: 'c'
|
80
|
-
|
81
|
-
"""
|
82
|
-
if not path:
|
83
|
-
return src
|
84
|
-
if isinstance(path, str):
|
85
|
-
path = path.split(".")
|
86
|
-
return find_path(src[path[0]], path[1:])
|
87
|
-
|
88
|
-
|
89
|
-
def is_valid_path(src, path):
|
90
|
-
"""
|
91
|
-
>>> is_valid_path(dict(a=1), 'a')
|
92
|
-
True
|
93
|
-
|
94
|
-
>>> is_valid_path(dict(a=1), '')
|
95
|
-
True
|
96
|
-
|
97
|
-
>>> is_valid_path(dict(a=1), None)
|
98
|
-
True
|
99
|
-
|
100
|
-
>>> is_valid_path(dict(a=1), 'b')
|
101
|
-
False
|
102
|
-
"""
|
103
|
-
try:
|
104
|
-
find_path(src, path)
|
105
|
-
return True
|
106
|
-
except KeyError:
|
107
|
-
return False
|
108
|
-
|
109
|
-
|
110
|
-
def camel2slug(s):
|
111
|
-
"""Convert camelCase to camel_case.
|
112
|
-
|
113
|
-
>>> camel2slug('fooBar')
|
114
|
-
'foo_bar'
|
115
|
-
"""
|
116
|
-
return re.sub("([A-Z])", "_\\1", s).lower().lstrip("_")
|
1
|
+
from datetime import date, datetime
|
2
|
+
from base64 import b64encode
|
3
|
+
from string import ascii_letters as letters, digits
|
4
|
+
from sys import argv
|
5
|
+
from os import environ as env
|
6
|
+
from os.path import join, dirname, expanduser
|
7
|
+
from itertools import product
|
8
|
+
import json
|
9
|
+
import logging
|
10
|
+
import re
|
11
|
+
|
12
|
+
_LOGGER = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
def read_config():
|
16
|
+
"""Read config from file."""
|
17
|
+
for directory, filename in product(
|
18
|
+
[
|
19
|
+
dirname(argv[0]),
|
20
|
+
expanduser("~"),
|
21
|
+
env.get("XDG_CONFIG_HOME", join(expanduser("~"), ".config")),
|
22
|
+
],
|
23
|
+
["seat.conf", ".seat.conf"],
|
24
|
+
):
|
25
|
+
try:
|
26
|
+
config = join(directory, filename)
|
27
|
+
_LOGGER.debug("checking for config file %s", config)
|
28
|
+
with open(config) as config:
|
29
|
+
return dict(
|
30
|
+
x.split(": ")
|
31
|
+
for x in config.read().strip().splitlines()
|
32
|
+
if not x.startswith("#")
|
33
|
+
)
|
34
|
+
except (IOError, OSError):
|
35
|
+
continue
|
36
|
+
return {}
|
37
|
+
|
38
|
+
|
39
|
+
def json_loads(s):
|
40
|
+
return json.loads(s, object_hook=obj_parser)
|
41
|
+
|
42
|
+
|
43
|
+
def obj_parser(obj):
|
44
|
+
"""Parse datetime."""
|
45
|
+
for key, val in obj.items():
|
46
|
+
try:
|
47
|
+
obj[key] = datetime.strptime(val, "%Y-%m-%dT%H:%M:%S%z")
|
48
|
+
except (TypeError, ValueError):
|
49
|
+
pass
|
50
|
+
return obj
|
51
|
+
|
52
|
+
|
53
|
+
def find_path(src, path):
|
54
|
+
"""Simple navigation of a hierarchical dict structure using XPATH-like syntax.
|
55
|
+
|
56
|
+
>>> find_path(dict(a=1), 'a')
|
57
|
+
1
|
58
|
+
|
59
|
+
>>> find_path(dict(a=1), '')
|
60
|
+
{'a': 1}
|
61
|
+
|
62
|
+
>>> find_path(dict(a=None), 'a')
|
63
|
+
|
64
|
+
|
65
|
+
>>> find_path(dict(a=1), 'b')
|
66
|
+
Traceback (most recent call last):
|
67
|
+
...
|
68
|
+
KeyError: 'b'
|
69
|
+
|
70
|
+
>>> find_path(dict(a=dict(b=1)), 'a.b')
|
71
|
+
1
|
72
|
+
|
73
|
+
>>> find_path(dict(a=dict(b=1)), 'a')
|
74
|
+
{'b': 1}
|
75
|
+
|
76
|
+
>>> find_path(dict(a=dict(b=1)), 'a.c')
|
77
|
+
Traceback (most recent call last):
|
78
|
+
...
|
79
|
+
KeyError: 'c'
|
80
|
+
|
81
|
+
"""
|
82
|
+
if not path:
|
83
|
+
return src
|
84
|
+
if isinstance(path, str):
|
85
|
+
path = path.split(".")
|
86
|
+
return find_path(src[path[0]], path[1:])
|
87
|
+
|
88
|
+
|
89
|
+
def is_valid_path(src, path):
|
90
|
+
"""
|
91
|
+
>>> is_valid_path(dict(a=1), 'a')
|
92
|
+
True
|
93
|
+
|
94
|
+
>>> is_valid_path(dict(a=1), '')
|
95
|
+
True
|
96
|
+
|
97
|
+
>>> is_valid_path(dict(a=1), None)
|
98
|
+
True
|
99
|
+
|
100
|
+
>>> is_valid_path(dict(a=1), 'b')
|
101
|
+
False
|
102
|
+
"""
|
103
|
+
try:
|
104
|
+
find_path(src, path)
|
105
|
+
return True
|
106
|
+
except KeyError:
|
107
|
+
return False
|
108
|
+
|
109
|
+
|
110
|
+
def camel2slug(s):
|
111
|
+
"""Convert camelCase to camel_case.
|
112
|
+
|
113
|
+
>>> camel2slug('fooBar')
|
114
|
+
'foo_bar'
|
115
|
+
"""
|
116
|
+
return re.sub("([A-Z])", "_\\1", s).lower().lstrip("_")
|
pycupra/vehicle.py
CHANGED
@@ -34,7 +34,7 @@ _LOGGER = logging.getLogger(__name__)
|
|
34
34
|
DATEZERO = datetime(1970,1,1)
|
35
35
|
class Vehicle:
|
36
36
|
def __init__(self, conn, data):
|
37
|
-
_LOGGER.debug(f'Creating Vehicle class object with data {data}')
|
37
|
+
_LOGGER.debug(conn.anonymise(f'Creating Vehicle class object with data {data}'))
|
38
38
|
self._connection = conn
|
39
39
|
self._url = data.get('vin', '')
|
40
40
|
self._connectivities = data.get('connectivities', '')
|
@@ -50,6 +50,7 @@ class Vehicle:
|
|
50
50
|
self._firebaseCredentialsFileName = None
|
51
51
|
self._firebaseLastMessageId = ''
|
52
52
|
self.firebaseStatus = FIREBASE_STATUS_NOT_INITIALISED
|
53
|
+
self.firebase = None
|
53
54
|
self.updateCallback = None
|
54
55
|
|
55
56
|
self._requests = {
|
@@ -184,7 +185,7 @@ class Vehicle:
|
|
184
185
|
if hasattr(self, '_last_full_update'):
|
185
186
|
_LOGGER.debug(f'last_full_update= {self._last_full_update}, fullUpdateExpired= {fullUpdateExpired}.')
|
186
187
|
if updateType!=1 and (hasattr(self, '_last_full_update') and self._last_full_update>fullUpdateExpired):
|
187
|
-
_LOGGER.debug(f'Just performed small update for vehicle with VIN {self.vin}.')
|
188
|
+
_LOGGER.debug(f'Just performed small update for vehicle with VIN {self._connection.anonymise(self.vin)}.')
|
188
189
|
return True
|
189
190
|
|
190
191
|
# Data to be updated less often
|
@@ -206,13 +207,13 @@ class Vehicle:
|
|
206
207
|
return_exceptions=True
|
207
208
|
)
|
208
209
|
self._last_full_update = datetime.now(tz=None)
|
209
|
-
_LOGGER.debug(f'Performed full update for vehicle with VIN {self.vin}.')
|
210
|
+
_LOGGER.debug(f'Performed full update for vehicle with VIN {self._connection.anonymise(self.vin)}.')
|
210
211
|
_LOGGER.debug(f'So far about {self._connection._sessionRequestCounter} API calls since {self._connection._sessionRequestTimestamp}.')
|
211
212
|
except:
|
212
213
|
raise SeatException("Update failed")
|
213
214
|
return True
|
214
215
|
else:
|
215
|
-
_LOGGER.info(f'Vehicle with VIN {self.vin} is deactivated.')
|
216
|
+
_LOGGER.info(f'Vehicle with VIN {self._connection.anonymise(self.vin)} is deactivated.')
|
216
217
|
return False
|
217
218
|
return True
|
218
219
|
|
@@ -290,6 +291,14 @@ class Vehicle:
|
|
290
291
|
if self._relevantCapabilties.get('vehicleHealthWarnings', {}).get('active', False):
|
291
292
|
data = await self._connection.getVehicleHealthWarnings(self.vin, self._apibase)
|
292
293
|
if data:
|
294
|
+
#warningsList = data.get('warninglights',{}).get('statuses',[])
|
295
|
+
#for i in range(len(warningsList)):
|
296
|
+
# _LOGGER.debug(f'Element {i} in warninglights: {warningsList[i]}')
|
297
|
+
# if isinstance(warningsList[i], dict):
|
298
|
+
# if warningsList[i].get('icon',''):
|
299
|
+
# #Value of icon is very long and can lead to problems
|
300
|
+
# _LOGGER.debug(f'Substituting value of icon by \'DELETED\'')
|
301
|
+
# data['warninglights']['statuses'][i]['icon']='DELETED'
|
293
302
|
self._states.update(data)
|
294
303
|
else:
|
295
304
|
_LOGGER.debug('Could not fetch vehicle health warnings')
|
@@ -1394,6 +1403,17 @@ class Vehicle:
|
|
1394
1403
|
# if car.get('deactivated', False):
|
1395
1404
|
# return True
|
1396
1405
|
|
1406
|
+
@property
|
1407
|
+
def brand(self):
|
1408
|
+
"""Return brand"""
|
1409
|
+
return self._specification.get('factoryModel', False).get('vehicleBrand', 'Unknown')
|
1410
|
+
|
1411
|
+
@property
|
1412
|
+
def is_brand_supported(self):
|
1413
|
+
"""Return true if brand is supported."""
|
1414
|
+
if self._specification.get('factoryModel', False).get('vehicleBrand', False):
|
1415
|
+
return True
|
1416
|
+
|
1397
1417
|
@property
|
1398
1418
|
def model(self):
|
1399
1419
|
"""Return model"""
|
@@ -2217,7 +2237,12 @@ class Vehicle:
|
|
2217
2237
|
def warnings(self):
|
2218
2238
|
"""Return warnings."""
|
2219
2239
|
if self.attrs.get('warninglights', {}).get('statuses',[]):
|
2220
|
-
|
2240
|
+
warningTextList = []
|
2241
|
+
for elem in self.attrs['warninglights']['statuses']:
|
2242
|
+
if isinstance(elem, dict):
|
2243
|
+
if elem.get('text',''):
|
2244
|
+
warningTextList.append(elem.get('text',''))
|
2245
|
+
return warningTextList
|
2221
2246
|
return 'No warnings'
|
2222
2247
|
|
2223
2248
|
@property
|
@@ -3120,6 +3145,27 @@ class Vehicle:
|
|
3120
3145
|
)
|
3121
3146
|
|
3122
3147
|
|
3148
|
+
async def stopFirebase(self):
|
3149
|
+
# Check if firebase is activated
|
3150
|
+
if self.firebaseStatus!= FIREBASE_STATUS_ACTIVATED:
|
3151
|
+
_LOGGER.info(f'No need to stop firebase. Firebase status={self.firebaseStatus}')
|
3152
|
+
return self.firebaseStatus
|
3153
|
+
|
3154
|
+
if self.firebase == None:
|
3155
|
+
_LOGGER.error(f'Internal error: Firebase status={self.firebaseStatus} but firebase variable not set. Setting firebase status back to not initialised.')
|
3156
|
+
self.firebaseStatus = FIREBASE_STATUS_NOT_INITIALISED
|
3157
|
+
return self.firebaseStatus
|
3158
|
+
|
3159
|
+
success = await self.firebase.firebaseStop()
|
3160
|
+
if not success:
|
3161
|
+
_LOGGER.warning('Stopping of firebase messaging failed.')
|
3162
|
+
return self.firebaseStatus
|
3163
|
+
|
3164
|
+
#await asyncio.sleep(5) # Wait to ignore the first notifications
|
3165
|
+
self.firebaseStatus = FIREBASE_STATUS_NOT_INITIALISED
|
3166
|
+
_LOGGER.info('Stopping of firebase messaging was successful.')
|
3167
|
+
return self.firebaseStatus
|
3168
|
+
|
3123
3169
|
async def initialiseFirebase(self, firebaseCredentialsFileName=None, updateCallback=None):
|
3124
3170
|
# Check if firebase shall be used
|
3125
3171
|
if firebaseCredentialsFileName == None:
|
@@ -3141,12 +3187,13 @@ class Vehicle:
|
|
3141
3187
|
subscribedBrand = credentials.get('subscription',{}).get('brand','')
|
3142
3188
|
if subscribedVin != '' and subscribedUserId != '':
|
3143
3189
|
if subscribedVin != self.vin or subscribedUserId != self._connection._user_id or subscribedBrand != self._connection._session_auth_brand:
|
3144
|
-
_LOGGER.debug(f'Change of vin, userId or brand. Deleting subscription for vin={subscribedVin} and userId={subscribedUserId}.')
|
3190
|
+
_LOGGER.debug(self._connection.anonymise(f'Change of vin, userId or brand. Deleting subscription for vin={subscribedVin} and userId={subscribedUserId}.'))
|
3145
3191
|
result = await self._connection.deleteSubscription(credentials)
|
3146
3192
|
|
3147
3193
|
# Start firebase
|
3148
|
-
|
3149
|
-
|
3194
|
+
if self.firebase == None:
|
3195
|
+
self.firebase = Firebase()
|
3196
|
+
success = await self.firebase.firebaseStart(self.onNotification, firebaseCredentialsFileName, brand=self._connection._session_auth_brand)
|
3150
3197
|
if not success:
|
3151
3198
|
self.firebaseStatus = FIREBASE_STATUS_ACTIVATION_FAILED
|
3152
3199
|
_LOGGER.warning('Activation of firebase messaging failed.')
|
@@ -3167,7 +3214,7 @@ class Vehicle:
|
|
3167
3214
|
loop = asyncio.get_running_loop()
|
3168
3215
|
await loop.run_in_executor(None, writeFCMCredsFile, credentials, firebaseCredentialsFileName)
|
3169
3216
|
|
3170
|
-
await asyncio.sleep(5) # Wait to
|
3217
|
+
await asyncio.sleep(5) # Wait to ignore the first notifications
|
3171
3218
|
self.firebaseStatus = FIREBASE_STATUS_ACTIVATED
|
3172
3219
|
_LOGGER.info('Activation of firebase messaging was successful.')
|
3173
3220
|
return self.firebaseStatus
|
@@ -3247,7 +3294,7 @@ class Vehicle:
|
|
3247
3294
|
await self.updateCallback(2)
|
3248
3295
|
else:
|
3249
3296
|
_LOGGER.debug(f'It is now {datetime.now(tz=None)}. Last get_charger was at {self._last_get_charger}. So no need to update.')
|
3250
|
-
elif type in ('climatisation-status-changed','climatisation-started', 'climatisation-stopped'):
|
3297
|
+
elif type in ('climatisation-status-changed','climatisation-started', 'climatisation-stopped', 'climatisation-settings-updated'):
|
3251
3298
|
if self._requests.get('climatisation', {}).get('id', None):
|
3252
3299
|
openRequest= self._requests.get('climatisation', {}).get('id', None)
|
3253
3300
|
if openRequest == requestId:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pycupra
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.6
|
4
4
|
Summary: A library to read and send vehicle data via Cupra/Seat portal using the same API calls as the MyCupra/MySeat mobile app.
|
5
5
|
Home-page: https://github.com/WulfgarW/pycupra
|
6
6
|
Author: WulfgarW
|
@@ -1,12 +1,12 @@
|
|
1
1
|
pycupra/__init__.py,sha256=p0880jPkLqOErX3u3qaLboBLOsEHFpe44axApdaGeqI,231
|
2
|
-
pycupra/__version__.py,sha256=
|
3
|
-
pycupra/connection.py,sha256=
|
2
|
+
pycupra/__version__.py,sha256=orABFX8h4AFdsffPt4uVXqXeC0Wz3ZJOeNAzyeNw7O8,207
|
3
|
+
pycupra/connection.py,sha256=cZIqsjEwA1rTkhVyjowrTdbWnviOXFSso-WR8FnSrmg,90610
|
4
4
|
pycupra/const.py,sha256=cJJ9xrof6HZ7ZE7nXnB6tU4qMbSadlN8mgs43Igy7Mo,10591
|
5
5
|
pycupra/dashboard.py,sha256=RlJPdTvJV7Urog5gCz_4pYWNd5_ApQIVJUCwJIi_7L8,43822
|
6
6
|
pycupra/exceptions.py,sha256=Nq_F79GP8wjHf5lpvPy9TbSIrRHAJrFMo0T1N9TcgSQ,2917
|
7
|
-
pycupra/firebase.py,sha256=
|
8
|
-
pycupra/utilities.py,sha256=
|
9
|
-
pycupra/vehicle.py,sha256=
|
7
|
+
pycupra/firebase.py,sha256=tuN_W3OX3h3-yfdprWzmCn6z_T-BMx-OpL7Z6hOA8Lc,3451
|
8
|
+
pycupra/utilities.py,sha256=6sDxWP13-XtxmqhuBJBGdVbkj48BQ9AxFMrBPxH0J7g,2679
|
9
|
+
pycupra/vehicle.py,sha256=stxeyn2vd2j2yQ3dWEmT9XX1aZNZ7FdYvxbIraF6M0o,157175
|
10
10
|
pycupra/firebase_messaging/__init__.py,sha256=oerLHWvEf4qRqu3GxSX6SLY_OYI430ydAiAhKtzyMEM,666
|
11
11
|
pycupra/firebase_messaging/android_checkin_pb2.py,sha256=-U1oGroFt3KRuGDieae3iTcux6mAfx1TFkE1Q35ul2E,2849
|
12
12
|
pycupra/firebase_messaging/android_checkin_pb2.pyi,sha256=7KL-zQIz2Zz7uftcLkv57Podzu-yk6trn50FN4X4A8E,9379
|
@@ -18,8 +18,8 @@ pycupra/firebase_messaging/fcmregister.py,sha256=yZngC-0ZfTygtjfdzg03OW_3xk2n_uS
|
|
18
18
|
pycupra/firebase_messaging/mcs_pb2.py,sha256=nwXY7IDgLYPxgpSGs6wyTSyYDdomQsyGqH8R8EgODLg,7733
|
19
19
|
pycupra/firebase_messaging/mcs_pb2.pyi,sha256=HfIhInC3wRg8_caKwUm-V3knE2jTdEQvBy6uXgQ5rHY,33959
|
20
20
|
pycupra/firebase_messaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
|
-
pycupra-0.1.
|
22
|
-
pycupra-0.1.
|
23
|
-
pycupra-0.1.
|
24
|
-
pycupra-0.1.
|
25
|
-
pycupra-0.1.
|
21
|
+
pycupra-0.1.6.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
22
|
+
pycupra-0.1.6.dist-info/METADATA,sha256=u9ZdIDY607S5-5APn3VwI1nJgvWHPngzptNHedmY_k4,3757
|
23
|
+
pycupra-0.1.6.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
24
|
+
pycupra-0.1.6.dist-info/top_level.txt,sha256=9Lbj_jG4JvpGwt6K3AwhWFc0XieDnuHFOP4x44wSXSQ,8
|
25
|
+
pycupra-0.1.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|