carconnectivity-connector-skoda 0.1a6__py3-none-any.whl → 0.1a8__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.
Potentially problematic release.
This version of carconnectivity-connector-skoda might be problematic. Click here for more details.
- {carconnectivity_connector_skoda-0.1a6.dist-info → carconnectivity_connector_skoda-0.1a8.dist-info}/METADATA +1 -1
- {carconnectivity_connector_skoda-0.1a6.dist-info → carconnectivity_connector_skoda-0.1a8.dist-info}/RECORD +8 -8
- carconnectivity_connectors/skoda/_version.py +1 -1
- carconnectivity_connectors/skoda/connector.py +52 -3
- carconnectivity_connectors/skoda/mqtt_client.py +52 -5
- {carconnectivity_connector_skoda-0.1a6.dist-info → carconnectivity_connector_skoda-0.1a8.dist-info}/LICENSE +0 -0
- {carconnectivity_connector_skoda-0.1a6.dist-info → carconnectivity_connector_skoda-0.1a8.dist-info}/WHEEL +0 -0
- {carconnectivity_connector_skoda-0.1a6.dist-info → carconnectivity_connector_skoda-0.1a8.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
carconnectivity_connectors/skoda/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
carconnectivity_connectors/skoda/_version.py,sha256=
|
|
2
|
+
carconnectivity_connectors/skoda/_version.py,sha256=1Bne8W1AOZAeLlex5uwZwWO7vLZJck6Qhm9vGpBLrnw,408
|
|
3
3
|
carconnectivity_connectors/skoda/capability.py,sha256=JlNEaisVYF8qWv0wNDHTaas36uIpTIQ3NVR69wesiYQ,4513
|
|
4
|
-
carconnectivity_connectors/skoda/connector.py,sha256=
|
|
5
|
-
carconnectivity_connectors/skoda/mqtt_client.py,sha256=
|
|
4
|
+
carconnectivity_connectors/skoda/connector.py,sha256=MTAVR9ysRBoEvOvPyXw75HALouLeMvRhAvC1lbvPBvI,45851
|
|
5
|
+
carconnectivity_connectors/skoda/mqtt_client.py,sha256=wfrhg9sxutIW166w1kJldsiUkqZ7wyyz-fOeorcwQvA,22547
|
|
6
6
|
carconnectivity_connectors/skoda/vehicle.py,sha256=H3GRDNimMghFwFi--y9BsgoSK3pMibNf_l6SsDN6gvQ,2759
|
|
7
7
|
carconnectivity_connectors/skoda/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
carconnectivity_connectors/skoda/auth/auth_util.py,sha256=dGLUbUre0HBsTg_Ii5vW34f8DLrCykYJYCyzEvUBBEE,4434
|
|
@@ -11,8 +11,8 @@ carconnectivity_connectors/skoda/auth/openid_session.py,sha256=PLWSSKw9Dg7hBbhzJ
|
|
|
11
11
|
carconnectivity_connectors/skoda/auth/session_manager.py,sha256=Uf1vujuDBYUCAXhYToOsZkgbTtfmY3Qe0ICTfwomBpI,2899
|
|
12
12
|
carconnectivity_connectors/skoda/auth/skoda_web_session.py,sha256=cjzMkzx473Sh-4RgZAQULeRRcxB1MboddldCVM_y5LE,10640
|
|
13
13
|
carconnectivity_connectors/skoda/auth/helpers/blacklist_retry.py,sha256=f3wsiY5bpHDBxp7Va1Mv9nKJ4u3qnCHZZmDu78_AhMk,1251
|
|
14
|
-
carconnectivity_connector_skoda-0.
|
|
15
|
-
carconnectivity_connector_skoda-0.
|
|
16
|
-
carconnectivity_connector_skoda-0.
|
|
17
|
-
carconnectivity_connector_skoda-0.
|
|
18
|
-
carconnectivity_connector_skoda-0.
|
|
14
|
+
carconnectivity_connector_skoda-0.1a8.dist-info/LICENSE,sha256=PIwI1alwDyOfvEQHdGCm2u9uf_mGE8030xZDfun0xTo,1071
|
|
15
|
+
carconnectivity_connector_skoda-0.1a8.dist-info/METADATA,sha256=33p7mubCMTleCCDsqVPOjSIN-nOxo6dqnjR7_yU-vec,5326
|
|
16
|
+
carconnectivity_connector_skoda-0.1a8.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
|
|
17
|
+
carconnectivity_connector_skoda-0.1a8.dist-info/top_level.txt,sha256=KqA8GviZsDH4PtmnwSQsz0HB_w-TWkeEHLIRNo5dTaI,27
|
|
18
|
+
carconnectivity_connector_skoda-0.1a8.dist-info/RECORD,,
|
|
@@ -14,7 +14,7 @@ from carconnectivity.vehicle import GenericVehicle
|
|
|
14
14
|
from carconnectivity.errors import AuthenticationError, TooManyRequestsError, RetrievalError, APIError, APICompatibilityError, \
|
|
15
15
|
TemporaryAuthenticationError, ConfigurationError
|
|
16
16
|
from carconnectivity.util import robust_time_parse, log_extra_keys, config_remove_credentials
|
|
17
|
-
from carconnectivity.units import Length
|
|
17
|
+
from carconnectivity.units import Length, Speed, Power
|
|
18
18
|
from carconnectivity.doors import Doors
|
|
19
19
|
from carconnectivity.windows import Windows
|
|
20
20
|
from carconnectivity.lights import Lights
|
|
@@ -267,7 +267,9 @@ class Connector(BaseConnector):
|
|
|
267
267
|
vehicle.license_plate._set_value(None) # pylint: disable=protected-access
|
|
268
268
|
|
|
269
269
|
log_extra_keys(LOG_API, 'vehicles', vehicle_dict, {'vin', 'licensePlate'})
|
|
270
|
-
self.fetch_vehicle_status(vehicle)
|
|
270
|
+
vehicle = self.fetch_vehicle_status(vehicle)
|
|
271
|
+
if isinstance(vehicle, SkodaElectricVehicle):
|
|
272
|
+
vehicle = self.fetch_charging(vehicle)
|
|
271
273
|
else:
|
|
272
274
|
raise APIError('Could not parse vehicle, vin missing')
|
|
273
275
|
for vin in set(garage.list_vehicle_vins()) - seen_vehicle_vins:
|
|
@@ -275,7 +277,53 @@ class Connector(BaseConnector):
|
|
|
275
277
|
if vehicle_to_remove is not None and vehicle_to_remove.is_managed_by_connector(self):
|
|
276
278
|
garage.remove_vehicle(vin)
|
|
277
279
|
|
|
278
|
-
def
|
|
280
|
+
def fetch_charging(self, vehicle: SkodaElectricVehicle) -> SkodaElectricVehicle:
|
|
281
|
+
"""
|
|
282
|
+
Fetches the charging information for a given Skoda electric vehicle.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
vehicle (SkodaElectricVehicle): The Skoda electric vehicle object.
|
|
286
|
+
|
|
287
|
+
Raises:
|
|
288
|
+
APIError: If the VIN is missing or if the carCapturedTimestamp is missing in the response data.
|
|
289
|
+
ValueError: If the vehicle has no charging object.
|
|
290
|
+
"""
|
|
291
|
+
vin = vehicle.vin.value
|
|
292
|
+
if vin is None:
|
|
293
|
+
raise APIError('VIN is missing')
|
|
294
|
+
if vehicle.charging is None:
|
|
295
|
+
raise ValueError('Vehicle has no charging object')
|
|
296
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}'
|
|
297
|
+
data: Dict[str, Any] | None = self._fetch_data(url, session=self.session)
|
|
298
|
+
if data is not None:
|
|
299
|
+
if 'carCapturedTimestamp' in data and data['carCapturedTimestamp'] is not None:
|
|
300
|
+
captured_at: datetime = robust_time_parse(data['carCapturedTimestamp'])
|
|
301
|
+
else:
|
|
302
|
+
raise APIError('Could not fetch charging, carCapturedTimestamp missing')
|
|
303
|
+
if 'status' in data and data['status'] is not None:
|
|
304
|
+
if 'chargingRateInKilometersPerHour' in data['status'] and data['status']['chargingRateInKilometersPerHour'] is not None:
|
|
305
|
+
# pylint: disable-next=protected-access
|
|
306
|
+
vehicle.charging.rate._set_value(value=data['status']['chargingRateInKilometersPerHour'], measured=captured_at, unit=Speed.KMH)
|
|
307
|
+
else:
|
|
308
|
+
vehicle.charging.rate._set_value(None, measured=captured_at, unit=Speed.KMH) # pylint: disable=protected-access
|
|
309
|
+
if 'chargePowerInKw' in data['status'] and data['status']['chargePowerInKw'] is not None:
|
|
310
|
+
# pylint: disable-next=protected-access
|
|
311
|
+
vehicle.charging.power._set_value(value=data['status']['chargePowerInKw'], measured=captured_at, unit=Power.KW)
|
|
312
|
+
else:
|
|
313
|
+
vehicle.charging.power._set_value(None, measured=captured_at, unit=Power.KW) # pylint: disable=protected-access
|
|
314
|
+
if 'remainingTimeToFullyChargedInMinutes' in data['status'] and data['status']['remainingTimeToFullyChargedInMinutes'] is not None:
|
|
315
|
+
remaining_duration: timedelta = timedelta(minutes=data['status']['remainingTimeToFullyChargedInMinutes'])
|
|
316
|
+
# pylint: disable-next=protected-access
|
|
317
|
+
vehicle.charging.remaining_duration._set_value(value=remaining_duration, measured=captured_at)
|
|
318
|
+
else:
|
|
319
|
+
vehicle.charging.remaining_duration._set_value(None, measured=captured_at) # pylint: disable=protected-access
|
|
320
|
+
log_extra_keys(LOG_API, 'status', data['status'], {'chargingRateInKilometersPerHour',
|
|
321
|
+
'chargePowerInKw',
|
|
322
|
+
'remainingTimeToFullyChargedInMinutes'})
|
|
323
|
+
log_extra_keys(LOG_API, 'charging data', data, {'carCapturedTimestamp', 'status'})
|
|
324
|
+
return vehicle
|
|
325
|
+
|
|
326
|
+
def fetch_vehicle_status(self, vehicle: SkodaVehicle) -> SkodaVehicle:
|
|
279
327
|
"""
|
|
280
328
|
Fetches the status of a vehicle from the Skoda API.
|
|
281
329
|
|
|
@@ -579,6 +627,7 @@ class Connector(BaseConnector):
|
|
|
579
627
|
vehicle.lights.lights = {}
|
|
580
628
|
log_extra_keys(LOG_API, 'lights', vehicle_status_data['lights'], {'overallStatus', 'lightsStatus'})
|
|
581
629
|
log_extra_keys(LOG_API, 'vehicles', vehicle_status_data, {'capturedAt', 'mileageInKm', 'status', 'doors', 'windows', 'lights'})
|
|
630
|
+
return vehicle
|
|
582
631
|
|
|
583
632
|
def _record_elapsed(self, elapsed: timedelta) -> None:
|
|
584
633
|
"""
|
|
@@ -2,24 +2,31 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
|
+
import re
|
|
5
6
|
import logging
|
|
6
7
|
import uuid
|
|
7
8
|
import ssl
|
|
9
|
+
import json
|
|
10
|
+
from datetime import timedelta
|
|
8
11
|
|
|
9
12
|
from paho.mqtt.client import Client
|
|
10
13
|
from paho.mqtt.enums import MQTTProtocolVersion, CallbackAPIVersion, MQTTErrorCode
|
|
11
14
|
|
|
12
15
|
from carconnectivity.observable import Observable
|
|
13
|
-
from carconnectivity.vehicle import GenericVehicle
|
|
16
|
+
from carconnectivity.vehicle import GenericVehicle, ElectricVehicle
|
|
17
|
+
from carconnectivity.drive import ElectricDrive
|
|
18
|
+
from carconnectivity.util import robust_time_parse, log_extra_keys
|
|
14
19
|
|
|
15
20
|
|
|
16
21
|
if TYPE_CHECKING:
|
|
17
|
-
from typing import Set
|
|
22
|
+
from typing import Set, Dict, Any, Optional
|
|
23
|
+
from datetime import datetime
|
|
18
24
|
|
|
19
25
|
from carconnectivity_connectors.skoda.connector import Connector
|
|
20
26
|
|
|
21
27
|
|
|
22
28
|
LOG: logging.Logger = logging.getLogger("carconnectivity.connectors.skoda.mqtt")
|
|
29
|
+
LOG_API: logging.Logger = logging.getLogger("carconnectivity.connectors.skoda-api-debug")
|
|
23
30
|
|
|
24
31
|
|
|
25
32
|
class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
@@ -377,7 +384,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
377
384
|
elif reason_code == 160:
|
|
378
385
|
LOG.error('Client disconnected: Maximum connect time')
|
|
379
386
|
else:
|
|
380
|
-
LOG.error('Client unexpectedly disconnected (%s), trying to reconnect', reason_code)
|
|
387
|
+
LOG.error('Client unexpectedly disconnected (%d: %s), trying to reconnect', reason_code, reason_code)
|
|
381
388
|
|
|
382
389
|
def _on_subscribe_callback(self, mqttc, obj, mid, reason_codes, properties) -> None:
|
|
383
390
|
"""
|
|
@@ -422,5 +429,45 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
422
429
|
"""
|
|
423
430
|
del mqttc # unused
|
|
424
431
|
del obj # unused
|
|
425
|
-
|
|
426
|
-
|
|
432
|
+
if len(msg.payload) == 0:
|
|
433
|
+
LOG_API.debug('MQTT topic %s: ignoring empty message', msg.topic)
|
|
434
|
+
return
|
|
435
|
+
|
|
436
|
+
# service_events
|
|
437
|
+
match = re.match(r'^(?P<user_id>[0-9a-fA-F-]+)/(?P<vin>[A-Z0-9]+)/service-event/(?P<service_event>\w+)$', msg.topic)
|
|
438
|
+
if match:
|
|
439
|
+
user_id: str = match.group('user_id')
|
|
440
|
+
vin: str = match.group('vin')
|
|
441
|
+
service_event: str = match.group('service_event')
|
|
442
|
+
data: Dict[str, Any] = json.loads(msg.payload)
|
|
443
|
+
if data is not None:
|
|
444
|
+
if 'timestamp' in data and data['timestamp'] is not None:
|
|
445
|
+
measured_at: datetime = robust_time_parse(data['timestamp'])
|
|
446
|
+
else:
|
|
447
|
+
measured_at: datetime = datetime.now()
|
|
448
|
+
if service_event == 'charging':
|
|
449
|
+
if 'name' in data and data['name'] == 'change-charge-mode' or data['name'] == 'change-soc':
|
|
450
|
+
if 'data' in data and data['data'] is not None:
|
|
451
|
+
vehicle: Optional[GenericVehicle] = self._skoda_connector.car_connectivity.garage.get_vehicle(vin)
|
|
452
|
+
if isinstance(vehicle, ElectricVehicle):
|
|
453
|
+
electric_drive: ElectricDrive = vehicle.get_electric_drive()
|
|
454
|
+
if electric_drive is not None:
|
|
455
|
+
if 'soc' in data['data'] and data['data']['soc'] is not None:
|
|
456
|
+
electric_drive.level._set_value(measured=measured_at, value=data['data']['soc']) # pylint: disable=protected-access
|
|
457
|
+
if 'chargedRange' in data['data'] and data['data']['chargedRange'] is not None:
|
|
458
|
+
# pylint: disable-next=protected-access
|
|
459
|
+
electric_drive.range._set_value(measured=measured_at, value=data['data']['chargedRange'])
|
|
460
|
+
if 'timeToFinish' in data['data'] and data['data']['timeToFinish'] is not None \
|
|
461
|
+
and vehicle.charging is not None:
|
|
462
|
+
remaining_duration: timedelta = timedelta(minutes=int(data['data']['timeToFinish']))
|
|
463
|
+
# pylint: disable-next=protected-access
|
|
464
|
+
vehicle.charging.remaining_duration._set_value(measured=measured_at, value=remaining_duration)
|
|
465
|
+
log_extra_keys(LOG_API, 'data', data['data'], {'vin', 'userId', 'soc', 'chargedRange', 'timeToFinish'})
|
|
466
|
+
LOG.debug('Received %s event for vehicle %s from user %s', data['name'], vin, user_id)
|
|
467
|
+
return
|
|
468
|
+
LOG_API.info('Received event name %s service event %s for vehicle %s from user %s: %s', data['name'],
|
|
469
|
+
service_event, vin, user_id, msg.payload)
|
|
470
|
+
return
|
|
471
|
+
LOG_API.info('Received unknown service event %s for vehicle %s from user %s: %s', service_event, vin, user_id, msg.payload)
|
|
472
|
+
return
|
|
473
|
+
LOG_API.info('I don\'t understand message %s: %s', msg.topic, msg.payload)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|