carconnectivity-connector-skoda 0.1a7__tar.gz → 0.1a9__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.
Potentially problematic release.
This version of carconnectivity-connector-skoda might be problematic. Click here for more details.
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/PKG-INFO +1 -1
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connector_skoda.egg-info/PKG-INFO +1 -1
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connector_skoda.egg-info/SOURCES.txt +1 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connectors/skoda/_version.py +1 -1
- carconnectivity_connector_skoda-0.1a9/src/carconnectivity_connectors/skoda/charging.py +67 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connectors/skoda/connector.py +143 -9
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connectors/skoda/mqtt_client.py +48 -16
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/.flake8 +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/.github/dependabot.yml +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/.github/workflows/build.yml +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/.github/workflows/build_and_publish.yml +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/.github/workflows/codeql-analysis.yml +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/.gitignore +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/LICENSE +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/Makefile +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/README.md +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/doc/Config.md +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/pyproject.toml +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/setup.cfg +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/setup_requirements.txt +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connector_skoda.egg-info/dependency_links.txt +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connector_skoda.egg-info/requires.txt +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connector_skoda.egg-info/top_level.txt +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connectors/skoda/__init__.py +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connectors/skoda/auth/__init__.py +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connectors/skoda/auth/auth_util.py +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connectors/skoda/auth/helpers/blacklist_retry.py +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connectors/skoda/auth/my_skoda_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connectors/skoda/auth/openid_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connectors/skoda/auth/session_manager.py +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connectors/skoda/auth/skoda_web_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connectors/skoda/capability.py +0 -0
- {carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/src/carconnectivity_connectors/skoda/vehicle.py +0 -0
|
@@ -20,6 +20,7 @@ src/carconnectivity_connector_skoda.egg-info/top_level.txt
|
|
|
20
20
|
src/carconnectivity_connectors/skoda/__init__.py
|
|
21
21
|
src/carconnectivity_connectors/skoda/_version.py
|
|
22
22
|
src/carconnectivity_connectors/skoda/capability.py
|
|
23
|
+
src/carconnectivity_connectors/skoda/charging.py
|
|
23
24
|
src/carconnectivity_connectors/skoda/connector.py
|
|
24
25
|
src/carconnectivity_connectors/skoda/mqtt_client.py
|
|
25
26
|
src/carconnectivity_connectors/skoda/vehicle.py
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for charging for skoda vehicles.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from enum import Enum
|
|
8
|
+
|
|
9
|
+
from carconnectivity.charging import Charging
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from typing import Dict
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SkodaCharging(Charging): # pylint: disable=too-many-instance-attributes
|
|
16
|
+
"""
|
|
17
|
+
SkodaCharging class for handling Skoda vehicle charging information.
|
|
18
|
+
|
|
19
|
+
This class extends the Charging class and includes an enumeration of various
|
|
20
|
+
charging states specific to Skoda vehicles.
|
|
21
|
+
"""
|
|
22
|
+
class SkodaChargingState(Enum,):
|
|
23
|
+
"""
|
|
24
|
+
Enum representing the various charging states for a Skoda vehicle.
|
|
25
|
+
|
|
26
|
+
Attributes:
|
|
27
|
+
OFF: The vehicle is not charging.
|
|
28
|
+
READY_FOR_CHARGING: The vehicle is ready to start charging.
|
|
29
|
+
NOT_READY_FOR_CHARGING: The vehicle is not ready to start charging.
|
|
30
|
+
CONSERVATION: The vehicle is in conservation mode.
|
|
31
|
+
CHARGE_PURPOSE_REACHED_NOT_CONSERVATION_CHARGING: The vehicle has reached its charging purpose and is not in conservation charging mode.
|
|
32
|
+
CHARGE_PURPOSE_REACHED_CONSERVATION: The vehicle has reached its charging purpose and is in conservation charging mode.
|
|
33
|
+
CHARGING: The vehicle is currently charging.
|
|
34
|
+
ERROR: There is an error in the charging process.
|
|
35
|
+
UNSUPPORTED: The charging state is unsupported.
|
|
36
|
+
DISCHARGING: The vehicle is discharging.
|
|
37
|
+
UNKNOWN: The charging state is unknown.
|
|
38
|
+
"""
|
|
39
|
+
OFF = 'off'
|
|
40
|
+
CONNECT_CABLE = 'connectCable'
|
|
41
|
+
READY_FOR_CHARGING = 'readyForCharging'
|
|
42
|
+
NOT_READY_FOR_CHARGING = 'notReadyForCharging'
|
|
43
|
+
CONSERVATION = 'conservation'
|
|
44
|
+
CHARGE_PURPOSE_REACHED_NOT_CONSERVATION_CHARGING = 'chargePurposeReachedAndNotConservationCharging'
|
|
45
|
+
CHARGE_PURPOSE_REACHED_CONSERVATION = 'chargePurposeReachedAndConservation'
|
|
46
|
+
CHARGING = 'charging'
|
|
47
|
+
ERROR = 'error'
|
|
48
|
+
UNSUPPORTED = 'unsupported'
|
|
49
|
+
DISCHARGING = 'discharging'
|
|
50
|
+
UNKNOWN = 'unknown charging state'
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# Mapping of Skoda charging states to generic charging states
|
|
54
|
+
mapping_skoda_charging_state: Dict[SkodaCharging.SkodaChargingState, Charging.ChargingState] = {
|
|
55
|
+
SkodaCharging.SkodaChargingState.OFF: Charging.ChargingState.OFF,
|
|
56
|
+
SkodaCharging.SkodaChargingState.CONNECT_CABLE: Charging.ChargingState.OFF,
|
|
57
|
+
SkodaCharging.SkodaChargingState.READY_FOR_CHARGING: Charging.ChargingState.READY_FOR_CHARGING,
|
|
58
|
+
SkodaCharging.SkodaChargingState.NOT_READY_FOR_CHARGING: Charging.ChargingState.OFF,
|
|
59
|
+
SkodaCharging.SkodaChargingState.CONSERVATION: Charging.ChargingState.CONSERVATION,
|
|
60
|
+
SkodaCharging.SkodaChargingState.CHARGE_PURPOSE_REACHED_NOT_CONSERVATION_CHARGING: Charging.ChargingState.READY_FOR_CHARGING,
|
|
61
|
+
SkodaCharging.SkodaChargingState.CHARGE_PURPOSE_REACHED_CONSERVATION: Charging.ChargingState.CONSERVATION,
|
|
62
|
+
SkodaCharging.SkodaChargingState.CHARGING: Charging.ChargingState.CHARGING,
|
|
63
|
+
SkodaCharging.SkodaChargingState.ERROR: Charging.ChargingState.ERROR,
|
|
64
|
+
SkodaCharging.SkodaChargingState.UNSUPPORTED: Charging.ChargingState.UNSUPPORTED,
|
|
65
|
+
SkodaCharging.SkodaChargingState.DISCHARGING: Charging.ChargingState.DISCHARGING,
|
|
66
|
+
SkodaCharging.SkodaChargingState.UNKNOWN: Charging.ChargingState.UNKNOWN
|
|
67
|
+
}
|
|
@@ -6,7 +6,7 @@ import threading
|
|
|
6
6
|
import os
|
|
7
7
|
import logging
|
|
8
8
|
import netrc
|
|
9
|
-
from datetime import datetime, timedelta
|
|
9
|
+
from datetime import datetime, timedelta, timezone
|
|
10
10
|
import requests
|
|
11
11
|
|
|
12
12
|
from carconnectivity.garage import Garage
|
|
@@ -20,12 +20,15 @@ from carconnectivity.windows import Windows
|
|
|
20
20
|
from carconnectivity.lights import Lights
|
|
21
21
|
from carconnectivity.drive import GenericDrive, ElectricDrive, CombustionDrive
|
|
22
22
|
from carconnectivity.attributes import BooleanAttribute, DurationAttribute
|
|
23
|
+
from carconnectivity.charging import Charging
|
|
24
|
+
from carconnectivity.position import Position
|
|
23
25
|
|
|
24
26
|
from carconnectivity_connectors.base.connector import BaseConnector
|
|
25
27
|
from carconnectivity_connectors.skoda.auth.session_manager import SessionManager, SessionUser, Service
|
|
26
28
|
from carconnectivity_connectors.skoda.auth.my_skoda_session import MySkodaSession
|
|
27
29
|
from carconnectivity_connectors.skoda.vehicle import SkodaVehicle, SkodaElectricVehicle, SkodaCombustionVehicle, SkodaHybridVehicle
|
|
28
30
|
from carconnectivity_connectors.skoda.capability import Capability
|
|
31
|
+
from carconnectivity_connectors.skoda.charging import SkodaCharging, mapping_skoda_charging_state
|
|
29
32
|
from carconnectivity_connectors.skoda._version import __version__
|
|
30
33
|
from carconnectivity_connectors.skoda.mqtt_client import SkodaMQTTClient
|
|
31
34
|
|
|
@@ -150,12 +153,17 @@ class Connector(BaseConnector):
|
|
|
150
153
|
|
|
151
154
|
def _background_loop(self) -> None:
|
|
152
155
|
self._stop_event.clear()
|
|
156
|
+
fetch: bool = True
|
|
153
157
|
while not self._stop_event.is_set():
|
|
154
158
|
interval = 300
|
|
155
159
|
try:
|
|
156
160
|
try:
|
|
157
|
-
|
|
158
|
-
|
|
161
|
+
if fetch:
|
|
162
|
+
self.fetch_all()
|
|
163
|
+
fetch = False
|
|
164
|
+
else:
|
|
165
|
+
self.update_vehicles()
|
|
166
|
+
self.last_update._set_value(value=datetime.now(tz=timezone.utc)) # pylint: disable=protected-access
|
|
159
167
|
if self.interval.value is not None:
|
|
160
168
|
interval: int = self.interval.value.total_seconds()
|
|
161
169
|
except Exception:
|
|
@@ -267,15 +275,37 @@ class Connector(BaseConnector):
|
|
|
267
275
|
vehicle.license_plate._set_value(None) # pylint: disable=protected-access
|
|
268
276
|
|
|
269
277
|
log_extra_keys(LOG_API, 'vehicles', vehicle_dict, {'vin', 'licensePlate'})
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
vehicle = self.fetch_charging(vehicle)
|
|
278
|
+
|
|
279
|
+
vehicle = self.fetch_vehicle_details(vehicle)
|
|
273
280
|
else:
|
|
274
281
|
raise APIError('Could not parse vehicle, vin missing')
|
|
275
282
|
for vin in set(garage.list_vehicle_vins()) - seen_vehicle_vins:
|
|
276
283
|
vehicle_to_remove = garage.get_vehicle(vin)
|
|
277
284
|
if vehicle_to_remove is not None and vehicle_to_remove.is_managed_by_connector(self):
|
|
278
285
|
garage.remove_vehicle(vin)
|
|
286
|
+
self.update_vehicles()
|
|
287
|
+
|
|
288
|
+
def update_vehicles(self) -> None:
|
|
289
|
+
"""
|
|
290
|
+
Updates the status of all vehicles in the garage managed by this connector.
|
|
291
|
+
|
|
292
|
+
This method iterates through all vehicle VINs in the garage, and for each vehicle that is
|
|
293
|
+
managed by this connector and is an instance of SkodaVehicle, it updates the vehicle's status
|
|
294
|
+
by fetching data from various APIs. If the vehicle is an instance of SkodaElectricVehicle,
|
|
295
|
+
it also fetches charging information.
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
None
|
|
299
|
+
"""
|
|
300
|
+
garage: Garage = self.car_connectivity.garage
|
|
301
|
+
for vin in set(garage.list_vehicle_vins()):
|
|
302
|
+
vehicle_to_update: Optional[GenericVehicle] = garage.get_vehicle(vin)
|
|
303
|
+
if vehicle_to_update is not None and isinstance(vehicle_to_update, SkodaVehicle) and vehicle_to_update.is_managed_by_connector(self):
|
|
304
|
+
vehicle_to_update = self.fetch_vehicle_status_second_api(vehicle_to_update)
|
|
305
|
+
vehicle_to_update = self.fetch_driving_range(vehicle_to_update)
|
|
306
|
+
vehicle_to_update = self.fetch_position(vehicle_to_update)
|
|
307
|
+
if isinstance(vehicle_to_update, SkodaElectricVehicle):
|
|
308
|
+
vehicle_to_update = self.fetch_charging(vehicle_to_update)
|
|
279
309
|
|
|
280
310
|
def fetch_charging(self, vehicle: SkodaElectricVehicle) -> SkodaElectricVehicle:
|
|
281
311
|
"""
|
|
@@ -301,6 +331,18 @@ class Connector(BaseConnector):
|
|
|
301
331
|
else:
|
|
302
332
|
raise APIError('Could not fetch charging, carCapturedTimestamp missing')
|
|
303
333
|
if 'status' in data and data['status'] is not None:
|
|
334
|
+
if 'state' in data['status'] and data['status']['state'] is not None:
|
|
335
|
+
if data['status']['state'] in [item.name for item in SkodaCharging.SkodaChargingState]:
|
|
336
|
+
skoda_charging_state = SkodaCharging.SkodaChargingState[data['status']['state']]
|
|
337
|
+
charging_state: Charging.ChargingState = mapping_skoda_charging_state[skoda_charging_state]
|
|
338
|
+
else:
|
|
339
|
+
LOG_API.info('Unkown charging state %s not in %s', data['status']['state'], str(SkodaCharging.SkodaChargingState))
|
|
340
|
+
charging_state = Charging.ChargingState.UNKNOWN
|
|
341
|
+
|
|
342
|
+
# pylint: disable-next=protected-access
|
|
343
|
+
vehicle.charging.state._set_value(value=charging_state, measured=captured_at)
|
|
344
|
+
else:
|
|
345
|
+
vehicle.charging.state._set_value(None, measured=captured_at) # pylint: disable=protected-access
|
|
304
346
|
if 'chargingRateInKilometersPerHour' in data['status'] and data['status']['chargingRateInKilometersPerHour'] is not None:
|
|
305
347
|
# pylint: disable-next=protected-access
|
|
306
348
|
vehicle.charging.rate._set_value(value=data['status']['chargingRateInKilometersPerHour'], measured=captured_at, unit=Speed.KMH)
|
|
@@ -319,13 +361,68 @@ class Connector(BaseConnector):
|
|
|
319
361
|
vehicle.charging.remaining_duration._set_value(None, measured=captured_at) # pylint: disable=protected-access
|
|
320
362
|
log_extra_keys(LOG_API, 'status', data['status'], {'chargingRateInKilometersPerHour',
|
|
321
363
|
'chargePowerInKw',
|
|
322
|
-
'remainingTimeToFullyChargedInMinutes'
|
|
364
|
+
'remainingTimeToFullyChargedInMinutes',
|
|
365
|
+
'state'})
|
|
323
366
|
log_extra_keys(LOG_API, 'charging data', data, {'carCapturedTimestamp', 'status'})
|
|
324
367
|
return vehicle
|
|
325
368
|
|
|
326
|
-
def
|
|
369
|
+
def fetch_position(self, vehicle: SkodaVehicle) -> SkodaVehicle:
|
|
370
|
+
"""
|
|
371
|
+
Fetches the position of the given Skoda vehicle and updates its position attributes.
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
vehicle (SkodaVehicle): The Skoda vehicle object containing the VIN and position attributes.
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
SkodaVehicle: The updated Skoda vehicle object with the fetched position data.
|
|
378
|
+
|
|
379
|
+
Raises:
|
|
380
|
+
APIError: If the VIN is missing.
|
|
381
|
+
ValueError: If the vehicle has no position object.
|
|
382
|
+
"""
|
|
383
|
+
vin = vehicle.vin.value
|
|
384
|
+
if vin is None:
|
|
385
|
+
raise APIError('VIN is missing')
|
|
386
|
+
if vehicle.position is None:
|
|
387
|
+
raise ValueError('Vehicle has no charging object')
|
|
388
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/maps/positions?vin={vin}'
|
|
389
|
+
data: Dict[str, Any] | None = self._fetch_data(url, session=self.session)
|
|
390
|
+
if data is not None:
|
|
391
|
+
if 'positions' in data and data['positions'] is not None:
|
|
392
|
+
for position_dict in data['positions']:
|
|
393
|
+
if 'type' in position_dict and position_dict['type'] == 'VEHICLE':
|
|
394
|
+
if 'gpsCoordinates' in position_dict and position_dict['gpsCoordinates'] is not None:
|
|
395
|
+
if 'latitude' in position_dict['gpsCoordinates'] and position_dict['gpsCoordinates']['latitude'] is not None:
|
|
396
|
+
latitude: Optional[float] = position_dict['gpsCoordinates']['latitude']
|
|
397
|
+
else:
|
|
398
|
+
latitude = None
|
|
399
|
+
if 'longitude' in position_dict['gpsCoordinates'] and position_dict['gpsCoordinates']['longitude'] is not None:
|
|
400
|
+
longitude: Optional[float] = position_dict['gpsCoordinates']['longitude']
|
|
401
|
+
else:
|
|
402
|
+
longitude = None
|
|
403
|
+
vehicle.position.latitude._set_value(latitude) # pylint: disable=protected-access
|
|
404
|
+
vehicle.position.longitude._set_value(longitude) # pylint: disable=protected-access
|
|
405
|
+
vehicle.position.position_type._set_value(Position.PositionType.PARKING) # pylint: disable=protected-access
|
|
406
|
+
else:
|
|
407
|
+
vehicle.position.latitude._set_value(None) # pylint: disable=protected-access
|
|
408
|
+
vehicle.position.longitude._set_value(None) # pylint: disable=protected-access
|
|
409
|
+
vehicle.position.position_type._set_value(None) # pylint: disable=protected-access
|
|
410
|
+
else:
|
|
411
|
+
vehicle.position.latitude._set_value(None) # pylint: disable=protected-access
|
|
412
|
+
vehicle.position.longitude._set_value(None) # pylint: disable=protected-access
|
|
413
|
+
vehicle.position.position_type._set_value(None) # pylint: disable=protected-access
|
|
414
|
+
log_extra_keys(LOG_API, 'positions', position_dict, {'type',
|
|
415
|
+
'gpsCoordinates',
|
|
416
|
+
'address'})
|
|
417
|
+
else:
|
|
418
|
+
vehicle.position.latitude._set_value(None) # pylint: disable=protected-access
|
|
419
|
+
vehicle.position.longitude._set_value(None) # pylint: disable=protected-access
|
|
420
|
+
vehicle.position.position_type._set_value(None) # pylint: disable=protected-access
|
|
421
|
+
return vehicle
|
|
422
|
+
|
|
423
|
+
def fetch_vehicle_details(self, vehicle: SkodaVehicle) -> SkodaVehicle:
|
|
327
424
|
"""
|
|
328
|
-
Fetches the
|
|
425
|
+
Fetches the details of a vehicle from the Skoda API.
|
|
329
426
|
|
|
330
427
|
Args:
|
|
331
428
|
vehicle (GenericVehicle): The vehicle object containing the VIN.
|
|
@@ -374,7 +471,30 @@ class Connector(BaseConnector):
|
|
|
374
471
|
else:
|
|
375
472
|
vehicle.model._set_value(None) # pylint: disable=protected-access
|
|
376
473
|
log_extra_keys(LOG_API, 'api/v2/garage/vehicles/VIN', vehicle_data, {'softwareVersion'})
|
|
474
|
+
return vehicle
|
|
475
|
+
|
|
476
|
+
def fetch_driving_range(self, vehicle: SkodaVehicle) -> SkodaVehicle:
|
|
477
|
+
"""
|
|
478
|
+
Fetches the driving range data for a given Skoda vehicle and updates the vehicle object accordingly.
|
|
377
479
|
|
|
480
|
+
Args:
|
|
481
|
+
vehicle (SkodaVehicle): The Skoda vehicle object for which to fetch the driving range data.
|
|
482
|
+
|
|
483
|
+
Returns:
|
|
484
|
+
SkodaVehicle: The updated Skoda vehicle object with the fetched driving range data.
|
|
485
|
+
|
|
486
|
+
Raises:
|
|
487
|
+
APIError: If the vehicle's VIN is missing.
|
|
488
|
+
|
|
489
|
+
Notes:
|
|
490
|
+
- The method fetches data from the Skoda API using the vehicle's VIN.
|
|
491
|
+
- It updates the vehicle's type if the fetched data indicates a different type (e.g., electric, combustion, hybrid).
|
|
492
|
+
- It updates the vehicle's total range and individual drive ranges (primary and secondary) based on the fetched data.
|
|
493
|
+
- It logs warnings for unknown car types and engine types.
|
|
494
|
+
"""
|
|
495
|
+
vin = vehicle.vin.value
|
|
496
|
+
if vin is None:
|
|
497
|
+
raise APIError('VIN is missing')
|
|
378
498
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/vehicle-status/{vin}/driving-range'
|
|
379
499
|
range_data: Dict[str, Any] | None = self._fetch_data(url, self.session)
|
|
380
500
|
if range_data:
|
|
@@ -461,7 +581,21 @@ class Connector(BaseConnector):
|
|
|
461
581
|
'totalRangeInKm',
|
|
462
582
|
'primaryEngineRange',
|
|
463
583
|
'secondaryEngineRange'})
|
|
584
|
+
return vehicle
|
|
464
585
|
|
|
586
|
+
def fetch_vehicle_status_second_api(self, vehicle: SkodaVehicle) -> SkodaVehicle:
|
|
587
|
+
"""
|
|
588
|
+
Fetches the status of a vehicle from other Skoda API.
|
|
589
|
+
|
|
590
|
+
Args:
|
|
591
|
+
vehicle (GenericVehicle): The vehicle object containing the VIN.
|
|
592
|
+
|
|
593
|
+
Returns:
|
|
594
|
+
None
|
|
595
|
+
"""
|
|
596
|
+
vin = vehicle.vin.value
|
|
597
|
+
if vin is None:
|
|
598
|
+
raise APIError('VIN is missing')
|
|
465
599
|
url = f'https://api.connect.skoda-auto.cz/api/v2/vehicle-status/{vin}'
|
|
466
600
|
vehicle_status_data: Dict[str, Any] | None = self._fetch_data(url, self.session)
|
|
467
601
|
if vehicle_status_data:
|
|
@@ -7,15 +7,21 @@ import logging
|
|
|
7
7
|
import uuid
|
|
8
8
|
import ssl
|
|
9
9
|
import json
|
|
10
|
-
from datetime import timedelta
|
|
10
|
+
from datetime import timedelta, timezone
|
|
11
11
|
|
|
12
12
|
from paho.mqtt.client import Client
|
|
13
13
|
from paho.mqtt.enums import MQTTProtocolVersion, CallbackAPIVersion, MQTTErrorCode
|
|
14
14
|
|
|
15
|
+
from carconnectivity.errors import CarConnectivityError
|
|
15
16
|
from carconnectivity.observable import Observable
|
|
16
|
-
from carconnectivity.vehicle import GenericVehicle
|
|
17
|
+
from carconnectivity.vehicle import GenericVehicle
|
|
18
|
+
|
|
17
19
|
from carconnectivity.drive import ElectricDrive
|
|
18
20
|
from carconnectivity.util import robust_time_parse, log_extra_keys
|
|
21
|
+
from carconnectivity.charging import Charging
|
|
22
|
+
|
|
23
|
+
from carconnectivity_connectors.skoda.vehicle import SkodaElectricVehicle
|
|
24
|
+
from carconnectivity_connectors.skoda.charging import SkodaCharging, mapping_skoda_charging_state
|
|
19
25
|
|
|
20
26
|
|
|
21
27
|
if TYPE_CHECKING:
|
|
@@ -101,7 +107,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
101
107
|
if (flags & Observable.ObserverEvent.ENABLED) and isinstance(element, GenericVehicle):
|
|
102
108
|
self._subscribe_vehicle(element)
|
|
103
109
|
elif (flags & Observable.ObserverEvent.DISABLED) and isinstance(element, GenericVehicle):
|
|
104
|
-
self.
|
|
110
|
+
self._unsubscribe_vehicle(element)
|
|
105
111
|
|
|
106
112
|
def _subscribe_vehicles(self) -> None:
|
|
107
113
|
"""
|
|
@@ -235,15 +241,12 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
235
241
|
- Warning if the vehicle's VIN is not enabled or is None.
|
|
236
242
|
- Info for each topic successfully unsubscribed.
|
|
237
243
|
"""
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
self.unsubscribe(topic)
|
|
245
|
-
self.subscribed_topics.remove(topic)
|
|
246
|
-
LOG.debug('Unsubscribed from topic %s', topic)
|
|
244
|
+
vin: str = vehicle.id
|
|
245
|
+
for topic in self.subscribed_topics:
|
|
246
|
+
if vin in topic:
|
|
247
|
+
self.unsubscribe(topic)
|
|
248
|
+
self.subscribed_topics.remove(topic)
|
|
249
|
+
LOG.debug('Unsubscribed from topic %s', topic)
|
|
247
250
|
|
|
248
251
|
def _on_connect_callback(self, mqttc, obj, flags, reason_code, properties) -> None:
|
|
249
252
|
"""
|
|
@@ -444,27 +447,56 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
444
447
|
if 'timestamp' in data and data['timestamp'] is not None:
|
|
445
448
|
measured_at: datetime = robust_time_parse(data['timestamp'])
|
|
446
449
|
else:
|
|
447
|
-
measured_at: datetime = datetime.now()
|
|
450
|
+
measured_at: datetime = datetime.now(tz=timezone.utc)
|
|
448
451
|
if service_event == 'charging':
|
|
449
452
|
if 'name' in data and data['name'] == 'change-charge-mode' or data['name'] == 'change-soc':
|
|
450
453
|
if 'data' in data and data['data'] is not None:
|
|
451
454
|
vehicle: Optional[GenericVehicle] = self._skoda_connector.car_connectivity.garage.get_vehicle(vin)
|
|
452
|
-
if isinstance(vehicle,
|
|
455
|
+
if isinstance(vehicle, SkodaElectricVehicle):
|
|
453
456
|
electric_drive: ElectricDrive = vehicle.get_electric_drive()
|
|
454
457
|
if electric_drive is not None:
|
|
458
|
+
charging_state: Optional[Charging.ChargingState] = vehicle.charging.state.value
|
|
459
|
+
old_charging_state: Optional[Charging.ChargingState] = charging_state
|
|
460
|
+
if 'state' in data['data'] and data['data']['state'] is not None:
|
|
461
|
+
if data['data']['state'] in [item.value for item in SkodaCharging.SkodaChargingState]:
|
|
462
|
+
skoda_charging_state = SkodaCharging.SkodaChargingState(data['data']['state'])
|
|
463
|
+
charging_state = mapping_skoda_charging_state[skoda_charging_state]
|
|
464
|
+
else:
|
|
465
|
+
LOG_API.info('Unkown charging state %s not in %s', data['data']['state'], str(SkodaCharging.SkodaChargingState))
|
|
466
|
+
charging_state = Charging.ChargingState.UNKNOWN
|
|
467
|
+
# pylint: disable-next=protected-access
|
|
468
|
+
vehicle.charging.state._set_value(value=charging_state, measured=measured_at)
|
|
469
|
+
if charging_state == Charging.ChargingState.OFF:
|
|
470
|
+
# pylint: disable-next=protected-access
|
|
471
|
+
vehicle.charging.type._set_value(value=Charging.ChargingType.OFF, measured=measured_at)
|
|
472
|
+
# pylint: disable-next=protected-access
|
|
473
|
+
vehicle.charging.rate._set_value(value=0, measured=measured_at)
|
|
474
|
+
# pylint: disable-next=protected-access
|
|
475
|
+
vehicle.charging.power._set_value(value=0, measured=measured_at)
|
|
455
476
|
if 'soc' in data['data'] and data['data']['soc'] is not None:
|
|
456
477
|
electric_drive.level._set_value(measured=measured_at, value=data['data']['soc']) # pylint: disable=protected-access
|
|
457
478
|
if 'chargedRange' in data['data'] and data['data']['chargedRange'] is not None:
|
|
458
479
|
# pylint: disable-next=protected-access
|
|
459
480
|
electric_drive.range._set_value(measured=measured_at, value=data['data']['chargedRange'])
|
|
481
|
+
# If charging state changed, fetch charging again
|
|
482
|
+
if old_charging_state != charging_state:
|
|
483
|
+
try:
|
|
484
|
+
self._skoda_connector.fetch_charging(vehicle)
|
|
485
|
+
except CarConnectivityError as e:
|
|
486
|
+
LOG.error('Error while fetching charging: %s', e)
|
|
460
487
|
if 'timeToFinish' in data['data'] and data['data']['timeToFinish'] is not None \
|
|
461
488
|
and vehicle.charging is not None:
|
|
462
|
-
|
|
489
|
+
try:
|
|
490
|
+
remaining_duration: Optional[timedelta] = timedelta(minutes=int(data['data']['timeToFinish']))
|
|
491
|
+
except ValueError:
|
|
492
|
+
remaining_duration: Optional[timedelta] = None
|
|
463
493
|
# pylint: disable-next=protected-access
|
|
464
494
|
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'})
|
|
495
|
+
log_extra_keys(LOG_API, 'data', data['data'], {'vin', 'userId', 'soc', 'chargedRange', 'timeToFinish', 'state'})
|
|
466
496
|
LOG.debug('Received %s event for vehicle %s from user %s', data['name'], vin, user_id)
|
|
467
497
|
return
|
|
498
|
+
else:
|
|
499
|
+
LOG.debug('Discarded %s event for vehicle %s from user %s: vehicle is not an electric vehicle', data['name'], vin, user_id)
|
|
468
500
|
LOG_API.info('Received event name %s service event %s for vehicle %s from user %s: %s', data['name'],
|
|
469
501
|
service_event, vin, user_id, msg.payload)
|
|
470
502
|
return
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/doc/Config.md
RENAMED
|
File without changes
|
{carconnectivity_connector_skoda-0.1a7 → carconnectivity_connector_skoda-0.1a9}/pyproject.toml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|