pycupra 0.1.13__py3-none-any.whl → 0.1.14__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 +12 -4
- example/PyCupra_ExportDrivingData.py +113 -0
- pycupra/connection.py +75 -67
- pycupra/dashboard.py +36 -36
- pycupra/firebase.py +11 -8
- pycupra/firebase_messaging/fcmpushclient.py +8 -7
- pycupra/firebase_messaging/fcmregister.py +3 -3
- pycupra/utilities.py +2 -26
- pycupra/vehicle.py +299 -226
- {pycupra-0.1.13.dist-info → pycupra-0.1.14.dist-info}/METADATA +6 -2
- {pycupra-0.1.13.dist-info → pycupra-0.1.14.dist-info}/RECORD +14 -13
- {pycupra-0.1.13.dist-info → pycupra-0.1.14.dist-info}/top_level.txt +1 -0
- {pycupra-0.1.13.dist-info → pycupra-0.1.14.dist-info}/WHEEL +0 -0
- {pycupra-0.1.13.dist-info → pycupra-0.1.14.dist-info}/licenses/LICENSE +0 -0
pycupra/dashboard.py
CHANGED
@@ -26,7 +26,7 @@ class Instrument:
|
|
26
26
|
def slug_attr(self):
|
27
27
|
return camel2slug(self.attr.replace(".", "_"))
|
28
28
|
|
29
|
-
def setup(self, vehicle, **config):
|
29
|
+
def setup(self, vehicle, **config) -> bool:
|
30
30
|
self.vehicle = vehicle
|
31
31
|
if not self.is_supported:
|
32
32
|
return False
|
@@ -78,7 +78,7 @@ class Sensor(Instrument):
|
|
78
78
|
self.unit = unit
|
79
79
|
self.convert = False
|
80
80
|
|
81
|
-
def configurate(self, **config):
|
81
|
+
def configurate(self, **config) -> None:
|
82
82
|
if self.unit and config.get('miles', False) is True:
|
83
83
|
if "km" == self.unit:
|
84
84
|
self.unit = "mi"
|
@@ -109,7 +109,7 @@ class Sensor(Instrument):
|
|
109
109
|
self.vehicle.pheater_duration = setValue
|
110
110
|
|
111
111
|
@property
|
112
|
-
def is_mutable(self):
|
112
|
+
def is_mutable(self) -> bool:
|
113
113
|
return False
|
114
114
|
|
115
115
|
@property
|
@@ -147,7 +147,7 @@ class BinarySensor(Instrument):
|
|
147
147
|
self.reverse_state = reverse_state
|
148
148
|
|
149
149
|
@property
|
150
|
-
def is_mutable(self):
|
150
|
+
def is_mutable(self) -> bool:
|
151
151
|
return False
|
152
152
|
|
153
153
|
@property
|
@@ -191,11 +191,11 @@ class Switch(Instrument):
|
|
191
191
|
super().__init__(component="switch", attr=attr, name=name, icon=icon)
|
192
192
|
|
193
193
|
@property
|
194
|
-
def is_mutable(self):
|
194
|
+
def is_mutable(self) -> bool:
|
195
195
|
return True
|
196
196
|
|
197
197
|
@property
|
198
|
-
def str_state(self):
|
198
|
+
def str_state(self) -> str:
|
199
199
|
return "On" if self.state else "Off"
|
200
200
|
|
201
201
|
def is_on(self):
|
@@ -208,7 +208,7 @@ class Switch(Instrument):
|
|
208
208
|
pass
|
209
209
|
|
210
210
|
@property
|
211
|
-
def assumed_state(self):
|
211
|
+
def assumed_state(self) -> bool:
|
212
212
|
return True
|
213
213
|
|
214
214
|
|
@@ -221,13 +221,13 @@ class Climate(Instrument):
|
|
221
221
|
pass
|
222
222
|
|
223
223
|
@property
|
224
|
-
def target_temperature(self):
|
224
|
+
def target_temperature(self) -> None:
|
225
225
|
pass
|
226
226
|
|
227
|
-
def set_temperature(self, **kwargs):
|
227
|
+
def set_temperature(self, **kwargs) -> None:
|
228
228
|
pass
|
229
229
|
|
230
|
-
def set_hvac_mode(self, hvac_mode):
|
230
|
+
def set_hvac_mode(self, hvac_mode) -> None:
|
231
231
|
pass
|
232
232
|
|
233
233
|
|
@@ -284,7 +284,7 @@ class Position(Instrument):
|
|
284
284
|
super().__init__(component="device_tracker", attr="position", name="Position")
|
285
285
|
|
286
286
|
@property
|
287
|
-
def is_mutable(self):
|
287
|
+
def is_mutable(self) -> bool:
|
288
288
|
return False
|
289
289
|
|
290
290
|
@property
|
@@ -298,7 +298,7 @@ class Position(Instrument):
|
|
298
298
|
)
|
299
299
|
|
300
300
|
@property
|
301
|
-
def str_state(self):
|
301
|
+
def str_state(self) -> tuple:
|
302
302
|
state = super().state #or {}
|
303
303
|
ts = state.get("timestamp", None)
|
304
304
|
if isinstance(ts, str):
|
@@ -323,11 +323,11 @@ class DoorLock(Instrument):
|
|
323
323
|
self.spin = config.get('spin', '')
|
324
324
|
|
325
325
|
@property
|
326
|
-
def is_mutable(self):
|
326
|
+
def is_mutable(self) -> bool:
|
327
327
|
return True
|
328
328
|
|
329
329
|
@property
|
330
|
-
def str_state(self):
|
330
|
+
def str_state(self) -> str:
|
331
331
|
return "Locked" if self.state else "Unlocked"
|
332
332
|
|
333
333
|
@property
|
@@ -400,21 +400,21 @@ class RequestHonkAndFlash(Switch):
|
|
400
400
|
def state(self):
|
401
401
|
return self.vehicle.request_honkandflash
|
402
402
|
|
403
|
-
async def turn_on(self):
|
403
|
+
async def turn_on(self) -> None:
|
404
404
|
await self.vehicle.set_honkandflash('honkandflash')
|
405
405
|
#await self.vehicle.update()
|
406
406
|
if self.callback is not None:
|
407
407
|
self.callback()
|
408
408
|
|
409
|
-
async def turn_off(self):
|
409
|
+
async def turn_off(self) -> None:
|
410
410
|
pass
|
411
411
|
|
412
412
|
@property
|
413
|
-
def assumed_state(self):
|
413
|
+
def assumed_state(self) -> bool:
|
414
414
|
return False
|
415
415
|
|
416
416
|
@property
|
417
|
-
def attributes(self):
|
417
|
+
def attributes(self) -> dict:
|
418
418
|
return dict(last_result = self.vehicle.honkandflash_action_status)
|
419
419
|
|
420
420
|
|
@@ -426,21 +426,21 @@ class RequestFlash(Switch):
|
|
426
426
|
def state(self):
|
427
427
|
return self.vehicle.request_flash
|
428
428
|
|
429
|
-
async def turn_on(self):
|
429
|
+
async def turn_on(self) -> None:
|
430
430
|
await self.vehicle.set_honkandflash('flash')
|
431
431
|
#await self.vehicle.update()
|
432
432
|
if self.callback is not None:
|
433
433
|
self.callback()
|
434
434
|
|
435
|
-
async def turn_off(self):
|
435
|
+
async def turn_off(self) -> None:
|
436
436
|
pass
|
437
437
|
|
438
438
|
@property
|
439
|
-
def assumed_state(self):
|
439
|
+
def assumed_state(self) -> bool:
|
440
440
|
return False
|
441
441
|
|
442
442
|
@property
|
443
|
-
def attributes(self):
|
443
|
+
def attributes(self) -> dict:
|
444
444
|
return dict(last_result = self.vehicle.honkandflash_action_status)
|
445
445
|
|
446
446
|
|
@@ -452,22 +452,22 @@ class RequestRefresh(Switch):
|
|
452
452
|
def state(self):
|
453
453
|
return self.vehicle.refresh_data
|
454
454
|
|
455
|
-
async def turn_on(self):
|
455
|
+
async def turn_on(self) -> None:
|
456
456
|
_LOGGER.debug('User has called RequestRefresh().')
|
457
457
|
await self.vehicle.set_refresh()
|
458
458
|
#await self.vehicle.update(updateType=1) #full update after set_refresh
|
459
459
|
if self.callback is not None:
|
460
460
|
self.callback()
|
461
461
|
|
462
|
-
async def turn_off(self):
|
462
|
+
async def turn_off(self) -> None:
|
463
463
|
pass
|
464
464
|
|
465
465
|
@property
|
466
|
-
def assumed_state(self):
|
466
|
+
def assumed_state(self) -> bool:
|
467
467
|
return False
|
468
468
|
|
469
469
|
@property
|
470
|
-
def attributes(self):
|
470
|
+
def attributes(self) -> dict:
|
471
471
|
return dict(last_result = self.vehicle.refresh_action_status)
|
472
472
|
|
473
473
|
|
@@ -476,20 +476,20 @@ class RequestUpdate(Switch):
|
|
476
476
|
super().__init__(attr="update_data", name="Request full update", icon="mdi:timer-refresh")
|
477
477
|
|
478
478
|
@property
|
479
|
-
def state(self):
|
479
|
+
def state(self) -> bool:
|
480
480
|
return False #self.vehicle.update
|
481
481
|
|
482
|
-
async def turn_on(self):
|
482
|
+
async def turn_on(self) -> None:
|
483
483
|
_LOGGER.debug('User has called RequestUpdate().')
|
484
484
|
await self.vehicle.update(updateType=1) #full update after set_refresh
|
485
485
|
if self.callback is not None:
|
486
486
|
self.callback()
|
487
487
|
|
488
|
-
async def turn_off(self):
|
488
|
+
async def turn_off(self) -> None:
|
489
489
|
pass
|
490
490
|
|
491
491
|
@property
|
492
|
-
def assumed_state(self):
|
492
|
+
def assumed_state(self) -> bool:
|
493
493
|
return False
|
494
494
|
|
495
495
|
#@property
|
@@ -514,11 +514,11 @@ class ElectricClimatisation(Switch):
|
|
514
514
|
#await self.vehicle.update()
|
515
515
|
|
516
516
|
@property
|
517
|
-
def assumed_state(self):
|
517
|
+
def assumed_state(self) -> bool:
|
518
518
|
return False
|
519
519
|
|
520
520
|
@property
|
521
|
-
def attributes(self):
|
521
|
+
def attributes(self) -> dict:
|
522
522
|
attrs = {}
|
523
523
|
if self.vehicle.is_electric_climatisation_attributes_supported:
|
524
524
|
attrs = self.vehicle.electric_climatisation_attributes
|
@@ -539,20 +539,20 @@ class AuxiliaryClimatisation(Switch):
|
|
539
539
|
def state(self):
|
540
540
|
return self.vehicle.auxiliary_climatisation
|
541
541
|
|
542
|
-
async def turn_on(self):
|
542
|
+
async def turn_on(self) -> None:
|
543
543
|
await self.vehicle.set_climatisation(mode = 'auxiliary', spin = self.spin)
|
544
544
|
#await self.vehicle.update()
|
545
545
|
|
546
|
-
async def turn_off(self):
|
546
|
+
async def turn_off(self) -> None:
|
547
547
|
await self.vehicle.set_climatisation(mode = 'off')
|
548
548
|
#await self.vehicle.update()
|
549
549
|
|
550
550
|
@property
|
551
|
-
def assumed_state(self):
|
551
|
+
def assumed_state(self) -> bool:
|
552
552
|
return False
|
553
553
|
|
554
554
|
@property
|
555
|
-
def attributes(self):
|
555
|
+
def attributes(self) -> dict:
|
556
556
|
return dict(last_result = self.vehicle.climater_action_status)
|
557
557
|
|
558
558
|
|
pycupra/firebase.py
CHANGED
@@ -6,6 +6,8 @@ import string
|
|
6
6
|
import secrets
|
7
7
|
|
8
8
|
from .firebase_messaging import FcmPushClient, FcmRegisterConfig
|
9
|
+
from typing import Any
|
10
|
+
|
9
11
|
|
10
12
|
from .const import (
|
11
13
|
FCM_PROJECT_ID,
|
@@ -19,14 +21,15 @@ class Firebase():
|
|
19
21
|
def __init__(self):
|
20
22
|
self._pushClient = None
|
21
23
|
|
22
|
-
async def firebaseStart(self, onNotificationFunc, firebaseCredentialsFileName, brand='cupra'):
|
24
|
+
async def firebaseStart(self, onNotificationFunc, firebaseCredentialsFileName: str, brand='cupra') -> bool:
|
23
25
|
""" Starts the firebase cloud messaging receiver """
|
24
26
|
try:
|
25
27
|
loop = asyncio.get_running_loop()
|
26
28
|
credentials = await loop.run_in_executor(None, readFCMCredsFile, firebaseCredentialsFileName)
|
27
29
|
#credentials = readFCMCredsFile(firebaseCredentialsFileName)
|
28
|
-
|
29
|
-
|
30
|
+
|
31
|
+
#if credentials == {}:
|
32
|
+
# credentials =None
|
30
33
|
|
31
34
|
fcm_project_id=FCM_PROJECT_ID
|
32
35
|
fcm_app_id=FCM_APP_ID[brand]
|
@@ -46,7 +49,7 @@ class Firebase():
|
|
46
49
|
_LOGGER.error(f'Error in firebaseStart. Error: {e}')
|
47
50
|
return False
|
48
51
|
|
49
|
-
async def firebaseStop(self):
|
52
|
+
async def firebaseStop(self) -> bool:
|
50
53
|
""" Stops the firebase cloud messaging receiver """
|
51
54
|
try:
|
52
55
|
await self._pushClient.stop()
|
@@ -57,7 +60,7 @@ class Firebase():
|
|
57
60
|
_LOGGER.error(f'Error in firebaseStop. Error: {e}')
|
58
61
|
return False
|
59
62
|
|
60
|
-
def readFCMCredsFile(credsFile):
|
63
|
+
def readFCMCredsFile(credsFile) -> dict[str, Any]:
|
61
64
|
""" Reads the firebase cloud messaging credentials from file"""
|
62
65
|
try:
|
63
66
|
if os.path.isfile(credsFile):
|
@@ -71,9 +74,9 @@ def readFCMCredsFile(credsFile):
|
|
71
74
|
return {}
|
72
75
|
except Exception as e:
|
73
76
|
_LOGGER.warning(f'readFCMCredsFile() not successful. Error: {e}')
|
74
|
-
return
|
77
|
+
return {}
|
75
78
|
|
76
|
-
def writeFCMCredsFile(creds, firebaseCredentialsFileName):
|
79
|
+
def writeFCMCredsFile(creds, firebaseCredentialsFileName) -> None:
|
77
80
|
""" Saves the firebase cloud messaging credentials to a file for future use """
|
78
81
|
try:
|
79
82
|
with open(firebaseCredentialsFileName, "w") as f:
|
@@ -82,7 +85,7 @@ def writeFCMCredsFile(creds, firebaseCredentialsFileName):
|
|
82
85
|
except Exception as e:
|
83
86
|
_LOGGER.warning(f'writeFCMCredsFile() not successful. Error: {e}')
|
84
87
|
|
85
|
-
async def onFCMCredentialsUpdated(creds, firebaseCredentialsFileName):
|
88
|
+
async def onFCMCredentialsUpdated(creds: dict[str, Any], firebaseCredentialsFileName: str) -> None:
|
86
89
|
""" Is called from firebase-messaging package """
|
87
90
|
loop = asyncio.get_running_loop()
|
88
91
|
await loop.run_in_executor(None, writeFCMCredsFile, creds, firebaseCredentialsFileName)
|
@@ -45,8 +45,8 @@ from .mcs_pb2 import ( # pylint: disable=no-name-in-module
|
|
45
45
|
|
46
46
|
_logger = logging.getLogger(__name__)
|
47
47
|
|
48
|
-
OnNotificationCallable = Callable[[
|
49
|
-
CredentialsUpdatedCallable = Callable[[dict[str, Any]], None]
|
48
|
+
OnNotificationCallable = Callable[[Any, str, Any], None]
|
49
|
+
CredentialsUpdatedCallable = Callable[[dict[str, Any], str], None]
|
50
50
|
|
51
51
|
# MCS Message Types and Tags
|
52
52
|
MCS_MESSAGE_TAG = {
|
@@ -144,10 +144,10 @@ class FcmPushClient: # pylint:disable=too-many-instance-attributes
|
|
144
144
|
|
145
145
|
def __init__(
|
146
146
|
self,
|
147
|
-
callback:
|
147
|
+
callback: OnNotificationCallable,
|
148
148
|
fcm_config: FcmRegisterConfig,
|
149
|
-
credentials: dict
|
150
|
-
credentials_updated_callback
|
149
|
+
credentials: dict,
|
150
|
+
credentials_updated_callback, #: CredentialsUpdatedCallable,
|
151
151
|
*,
|
152
152
|
callback_context: object | None = None,
|
153
153
|
received_persistent_ids: list[str] | None = None,
|
@@ -451,7 +451,8 @@ class FcmPushClient: # pylint:disable=too-many-instance-attributes
|
|
451
451
|
"Decrypted data for message %s is: %s", msg.persistent_id, ret_val
|
452
452
|
)
|
453
453
|
try:
|
454
|
-
|
454
|
+
if True: #self.callback!= None:
|
455
|
+
await self.callback(ret_val, msg.persistent_id, self.callback_context)
|
455
456
|
self._reset_error_count(ErrorType.NOTIFY)
|
456
457
|
except Exception:
|
457
458
|
_logger.exception("Unexpected exception calling notification callback\n")
|
@@ -611,7 +612,7 @@ class FcmPushClient: # pylint:disable=too-many-instance-attributes
|
|
611
612
|
self._send_selective_ack(msg.persistent_id),
|
612
613
|
return_exceptions=True
|
613
614
|
)
|
614
|
-
self.persistent_ids.append(msg.persistent_id)
|
615
|
+
self.persistent_ids.append(msg.persistent_id)
|
615
616
|
else:
|
616
617
|
await self._handle_data_message(msg)
|
617
618
|
self.persistent_ids.append(msg.persistent_id)
|
@@ -63,8 +63,8 @@ class FcmRegister:
|
|
63
63
|
def __init__(
|
64
64
|
self,
|
65
65
|
config: FcmRegisterConfig,
|
66
|
-
credentials: dict
|
67
|
-
credentials_updated_callback
|
66
|
+
credentials: dict,
|
67
|
+
credentials_updated_callback, #: Callable[[dict[str, Any], str], None] ,
|
68
68
|
*,
|
69
69
|
http_client_session: ClientSession | None = None,
|
70
70
|
log_debug_verbose: bool = False,
|
@@ -456,7 +456,7 @@ class FcmRegister:
|
|
456
456
|
return self.credentials
|
457
457
|
|
458
458
|
self.credentials = await self.register()
|
459
|
-
if self.credentials_updated_callback:
|
459
|
+
if True: #self.credentials_updated_callback != None:
|
460
460
|
await self.credentials_updated_callback(self.credentials, fcmCredentialsFileName)
|
461
461
|
|
462
462
|
return self.credentials
|
pycupra/utilities.py
CHANGED
@@ -12,30 +12,6 @@ import re
|
|
12
12
|
_LOGGER = logging.getLogger(__name__)
|
13
13
|
|
14
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
15
|
def json_loads(s):
|
40
16
|
return json.loads(s, object_hook=obj_parser)
|
41
17
|
|
@@ -86,7 +62,7 @@ def find_path(src, path):
|
|
86
62
|
return find_path(src[path[0]], path[1:])
|
87
63
|
|
88
64
|
|
89
|
-
def is_valid_path(src, path):
|
65
|
+
def is_valid_path(src, path) -> bool:
|
90
66
|
"""
|
91
67
|
>>> is_valid_path(dict(a=1), 'a')
|
92
68
|
True
|
@@ -107,7 +83,7 @@ def is_valid_path(src, path):
|
|
107
83
|
return False
|
108
84
|
|
109
85
|
|
110
|
-
def camel2slug(s):
|
86
|
+
def camel2slug(s) -> str:
|
111
87
|
"""Convert camelCase to camel_case.
|
112
88
|
|
113
89
|
>>> camel2slug('fooBar')
|