carconnectivity-connector-skoda 0.4a2__tar.gz → 0.4a4__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.4a2 → carconnectivity_connector_skoda-0.4a4}/PKG-INFO +2 -2
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/pyproject.toml +1 -1
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connector_skoda.egg-info/PKG-INFO +2 -2
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connector_skoda.egg-info/requires.txt +1 -1
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/_version.py +1 -1
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/auth/skoda_web_session.py +2 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/connector.py +243 -82
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/vehicle.py +4 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/.flake8 +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/.github/dependabot.yml +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/.github/workflows/build.yml +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/.github/workflows/build_and_publish.yml +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/.github/workflows/codeql-analysis.yml +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/.gitignore +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/CHANGELOG.md +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/LICENSE +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/Makefile +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/README.md +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/doc/Config.md +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/setup.cfg +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/setup_requirements.txt +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connector_skoda.egg-info/SOURCES.txt +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connector_skoda.egg-info/dependency_links.txt +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connector_skoda.egg-info/top_level.txt +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/__init__.py +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/auth/__init__.py +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/auth/auth_util.py +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/auth/helpers/blacklist_retry.py +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/auth/my_skoda_session.py +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/auth/openid_session.py +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/auth/session_manager.py +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/capability.py +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/charging.py +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/climatization.py +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/command_impl.py +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/error.py +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/mqtt_client.py +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/src/carconnectivity_connectors/skoda/ui/connector_ui.py +0 -0
- {carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/test/integration_test/carConnectivity.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: carconnectivity-connector-skoda
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4a4
|
|
4
4
|
Summary: CarConnectivity connector for Skoda services
|
|
5
5
|
Author: Till Steinbach
|
|
6
6
|
License: MIT License
|
|
@@ -37,7 +37,7 @@ Classifier: Topic :: Software Development :: Libraries
|
|
|
37
37
|
Requires-Python: >=3.8
|
|
38
38
|
Description-Content-Type: text/markdown
|
|
39
39
|
License-File: LICENSE
|
|
40
|
-
Requires-Dist: carconnectivity>=0.
|
|
40
|
+
Requires-Dist: carconnectivity>=0.4a6
|
|
41
41
|
Requires-Dist: oauthlib~=3.2.2
|
|
42
42
|
Requires-Dist: requests~=2.32.3
|
|
43
43
|
Requires-Dist: jwt~=1.3.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: carconnectivity-connector-skoda
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4a4
|
|
4
4
|
Summary: CarConnectivity connector for Skoda services
|
|
5
5
|
Author: Till Steinbach
|
|
6
6
|
License: MIT License
|
|
@@ -37,7 +37,7 @@ Classifier: Topic :: Software Development :: Libraries
|
|
|
37
37
|
Requires-Python: >=3.8
|
|
38
38
|
Description-Content-Type: text/markdown
|
|
39
39
|
License-File: LICENSE
|
|
40
|
-
Requires-Dist: carconnectivity>=0.
|
|
40
|
+
Requires-Dist: carconnectivity>=0.4a6
|
|
41
41
|
Requires-Dist: oauthlib~=3.2.2
|
|
42
42
|
Requires-Dist: requests~=2.32.3
|
|
43
43
|
Requires-Dist: jwt~=1.3.1
|
|
@@ -121,6 +121,8 @@ class SkodaWebSession(OpenIDSession):
|
|
|
121
121
|
raise RetrievalError('Temporary server error during login')
|
|
122
122
|
|
|
123
123
|
if 'Location' not in response.headers:
|
|
124
|
+
if 'consent' in url:
|
|
125
|
+
raise AuthenticationError('Could not find Location in headers, probably due to missing consent. Try visiting: ' + url)
|
|
124
126
|
raise APICompatibilityError('Forwarding without Location in headers')
|
|
125
127
|
|
|
126
128
|
url = response.headers['Location']
|
|
@@ -22,7 +22,7 @@ from carconnectivity.doors import Doors
|
|
|
22
22
|
from carconnectivity.windows import Windows
|
|
23
23
|
from carconnectivity.lights import Lights
|
|
24
24
|
from carconnectivity.drive import GenericDrive, ElectricDrive, CombustionDrive
|
|
25
|
-
from carconnectivity.attributes import BooleanAttribute, DurationAttribute, TemperatureAttribute
|
|
25
|
+
from carconnectivity.attributes import GenericAttribute, BooleanAttribute, DurationAttribute, TemperatureAttribute
|
|
26
26
|
from carconnectivity.charging import Charging
|
|
27
27
|
from carconnectivity.position import Position
|
|
28
28
|
from carconnectivity.climatization import Climatization
|
|
@@ -81,6 +81,16 @@ class Connector(BaseConnector):
|
|
|
81
81
|
|
|
82
82
|
self.connected: BooleanAttribute = BooleanAttribute(name="connected", parent=self, tags={'connector_custom'})
|
|
83
83
|
self.interval: DurationAttribute = DurationAttribute(name="interval", parent=self, tags={'connector_custom'})
|
|
84
|
+
|
|
85
|
+
def __check_interval(attribute: GenericAttribute, value: Any) -> Any:
|
|
86
|
+
del attribute
|
|
87
|
+
if value is not None and value < timedelta(seconds=180):
|
|
88
|
+
raise ValueError('Intervall must be at least 180 seconds')
|
|
89
|
+
return value
|
|
90
|
+
|
|
91
|
+
self.interval._is_changeable = True # pylint: disable=protected-access
|
|
92
|
+
self.interval._add_on_set_hook(__check_interval) # pylint: disable=protected-access
|
|
93
|
+
|
|
84
94
|
self.commands: Commands = Commands(parent=self)
|
|
85
95
|
|
|
86
96
|
self.user_id: Optional[str] = None
|
|
@@ -154,9 +164,11 @@ class Connector(BaseConnector):
|
|
|
154
164
|
self._stop_event.clear()
|
|
155
165
|
# Start background thread for Rest API polling
|
|
156
166
|
self._background_thread = threading.Thread(target=self._background_loop, daemon=False)
|
|
167
|
+
self._background_thread.name = 'carconnectivity.connectors.skoda-background'
|
|
157
168
|
self._background_thread.start()
|
|
158
169
|
# Start background thread for MQTT connection
|
|
159
170
|
self._background_connect_thread = threading.Thread(target=self._background_connect_loop, daemon=False)
|
|
171
|
+
self._background_connect_thread.name = 'carconnectivity.connectors.skoda-background_connect'
|
|
160
172
|
self._background_connect_thread.start()
|
|
161
173
|
# Start MQTT thread
|
|
162
174
|
self._mqtt_client.loop_start()
|
|
@@ -236,12 +248,12 @@ class Connector(BaseConnector):
|
|
|
236
248
|
self.car_connectivity.garage.remove_vehicle(vehicle.id)
|
|
237
249
|
vehicle.enabled = False
|
|
238
250
|
self._stop_event.set()
|
|
251
|
+
self.session.close()
|
|
239
252
|
if self._background_thread is not None:
|
|
240
253
|
self._background_thread.join()
|
|
241
254
|
if self._background_connect_thread is not None:
|
|
242
255
|
self._background_connect_thread.join()
|
|
243
256
|
self.persist()
|
|
244
|
-
self.session.close()
|
|
245
257
|
return super().shutdown()
|
|
246
258
|
|
|
247
259
|
def fetch_all(self) -> None:
|
|
@@ -339,6 +351,8 @@ class Connector(BaseConnector):
|
|
|
339
351
|
vehicle_to_update = self.fetch_vehicle_status(vehicle_to_update)
|
|
340
352
|
vehicle_to_update = self.fetch_driving_range(vehicle_to_update)
|
|
341
353
|
if vehicle_to_update.capabilities is not None and vehicle_to_update.capabilities.enabled:
|
|
354
|
+
if vehicle_to_update.capabilities.has_capability('READINESS'):
|
|
355
|
+
vehicle_to_update = self.fetch_connection_status(vehicle_to_update)
|
|
342
356
|
if vehicle_to_update.capabilities.has_capability('PARKING_POSITION'):
|
|
343
357
|
vehicle_to_update = self.fetch_position(vehicle_to_update)
|
|
344
358
|
if vehicle_to_update.capabilities.has_capability('CHARGING') and isinstance(vehicle_to_update, SkodaElectricVehicle):
|
|
@@ -347,6 +361,27 @@ class Connector(BaseConnector):
|
|
|
347
361
|
vehicle_to_update = self.fetch_air_conditioning(vehicle_to_update)
|
|
348
362
|
if vehicle_to_update.capabilities.has_capability('VEHICLE_HEALTH_INSPECTION'):
|
|
349
363
|
vehicle_to_update = self.fetch_maintenance(vehicle_to_update)
|
|
364
|
+
vehicle_to_update = self.decide_state(vehicle_to_update)
|
|
365
|
+
|
|
366
|
+
def decide_state(self, vehicle: SkodaVehicle) -> SkodaVehicle:
|
|
367
|
+
"""
|
|
368
|
+
Decides the state of the vehicle based on the current data.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
vehicle (SkodaVehicle): The Skoda vehicle object.
|
|
372
|
+
|
|
373
|
+
Returns:
|
|
374
|
+
SkodaVehicle: The Skoda vehicle object with the updated state.
|
|
375
|
+
"""
|
|
376
|
+
if vehicle is not None:
|
|
377
|
+
if vehicle.in_motion is not None and vehicle.in_motion.enabled and vehicle.in_motion.value:
|
|
378
|
+
vehicle.state._set_value(GenericVehicle.State.IGNITION_ON) # pylint: disable=protected-access
|
|
379
|
+
elif vehicle.position is not None and vehicle.position.enabled and vehicle.position.position_type is not None \
|
|
380
|
+
and vehicle.position.position_type.enabled and vehicle.position.position_type.value == Position.PositionType.PARKING:
|
|
381
|
+
vehicle.state._set_value(GenericVehicle.State.PARKED) # pylint: disable=protected-access
|
|
382
|
+
else:
|
|
383
|
+
vehicle.state._set_value(None) # pylint: disable=protected-access
|
|
384
|
+
return vehicle
|
|
350
385
|
|
|
351
386
|
def fetch_charging(self, vehicle: SkodaElectricVehicle, no_cache: bool = False) -> SkodaElectricVehicle:
|
|
352
387
|
"""
|
|
@@ -555,6 +590,41 @@ class Connector(BaseConnector):
|
|
|
555
590
|
vehicle.charging.errors.clear()
|
|
556
591
|
log_extra_keys(LOG_API, 'charging data', data, {'carCapturedTimestamp', 'status', 'isVehicleInSavedLocation', 'errors', 'settings'})
|
|
557
592
|
return vehicle
|
|
593
|
+
|
|
594
|
+
def fetch_connection_status(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
|
|
595
|
+
"""
|
|
596
|
+
Fetches the connection status of the given Skoda vehicle and updates its connection attributes.
|
|
597
|
+
|
|
598
|
+
Args:
|
|
599
|
+
vehicle (SkodaVehicle): The Skoda vehicle object containing the VIN and connection attributes.
|
|
600
|
+
|
|
601
|
+
Returns:
|
|
602
|
+
SkodaVehicle: The updated Skoda vehicle object with the fetched connection data.
|
|
603
|
+
|
|
604
|
+
Raises:
|
|
605
|
+
APIError: If the VIN is missing.
|
|
606
|
+
ValueError: If the vehicle has no connection object.
|
|
607
|
+
"""
|
|
608
|
+
vin = vehicle.vin.value
|
|
609
|
+
if vin is None:
|
|
610
|
+
raise APIError('VIN is missing')
|
|
611
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/connection-status/{vin}/readiness'
|
|
612
|
+
data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
|
613
|
+
# {'unreachable': False, 'inMotion': False, 'batteryProtectionLimitOn': False}
|
|
614
|
+
if data is not None:
|
|
615
|
+
if 'unreachable' in data and data['unreachable'] is not None:
|
|
616
|
+
if data['unreachable']:
|
|
617
|
+
vehicle.connection_state._set_value(vehicle.ConnectionState.OFFLINE) # pylint: disable=protected-access
|
|
618
|
+
else:
|
|
619
|
+
vehicle.connection_state._set_value(vehicle.ConnectionState.REACHABLE) # pylint: disable=protected-access
|
|
620
|
+
else:
|
|
621
|
+
vehicle.connection_state._set_value(None) # pylint: disable=protected-access
|
|
622
|
+
if 'inMotion' in data and data['inMotion'] is not None:
|
|
623
|
+
vehicle.in_motion._set_value(data['inMotion']) # pylint: disable=protected-access
|
|
624
|
+
else:
|
|
625
|
+
vehicle.in_motion._set_value(None) # pylint: disable=protected-access
|
|
626
|
+
log_extra_keys(LOG_API, 'connection status', data, {'unreachable'})
|
|
627
|
+
return vehicle
|
|
558
628
|
|
|
559
629
|
def fetch_position(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
|
|
560
630
|
"""
|
|
@@ -1336,10 +1406,20 @@ class Connector(BaseConnector):
|
|
|
1336
1406
|
raise SetterError(f'Unknown temperature unit {temperature_attribute.unit}')
|
|
1337
1407
|
|
|
1338
1408
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/settings/target-temperature'
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1409
|
+
try:
|
|
1410
|
+
settings_response: requests.Response = self.session.post(url, data=json.dumps(setting_dict), allow_redirects=True)
|
|
1411
|
+
if settings_response.status_code != requests.codes['accepted']:
|
|
1412
|
+
LOG.error('Could not set target temperature (%s)', settings_response.status_code)
|
|
1413
|
+
raise SetterError(f'Could not set value ({settings_response.status_code})')
|
|
1414
|
+
except requests.exceptions.ConnectionError as connection_error:
|
|
1415
|
+
raise SetterError(f'Connection error: {connection_error}.'
|
|
1416
|
+
' If this happens frequently, please check if other applications communicate with the Skoda server.') from connection_error
|
|
1417
|
+
except requests.exceptions.ChunkedEncodingError as chunked_encoding_error:
|
|
1418
|
+
raise SetterError(f'Error: {chunked_encoding_error}') from chunked_encoding_error
|
|
1419
|
+
except requests.exceptions.ReadTimeout as timeout_error:
|
|
1420
|
+
raise SetterError(f'Timeout during read: {timeout_error}') from timeout_error
|
|
1421
|
+
except requests.exceptions.RetryError as retry_error:
|
|
1422
|
+
raise SetterError(f'Retrying failed: {retry_error}') from retry_error
|
|
1343
1423
|
return target_temperature
|
|
1344
1424
|
|
|
1345
1425
|
def __on_air_conditioning_at_unlock_change(self, at_unlock_attribute: BooleanAttribute, at_unlock_value: bool) -> bool:
|
|
@@ -1355,10 +1435,20 @@ class Connector(BaseConnector):
|
|
|
1355
1435
|
setting_dict['airConditioningAtUnlockEnabled'] = at_unlock_value
|
|
1356
1436
|
|
|
1357
1437
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/settings/ac-at-unlock'
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1438
|
+
try:
|
|
1439
|
+
settings_response: requests.Response = self.session.post(url, data=json.dumps(setting_dict), allow_redirects=True)
|
|
1440
|
+
if settings_response.status_code != requests.codes['accepted']:
|
|
1441
|
+
LOG.error('Could not set air conditioning at unlock (%s)', settings_response.status_code)
|
|
1442
|
+
raise SetterError(f'Could not set value ({settings_response.status_code})')
|
|
1443
|
+
except requests.exceptions.ConnectionError as connection_error:
|
|
1444
|
+
raise SetterError(f'Connection error: {connection_error}.'
|
|
1445
|
+
' If this happens frequently, please check if other applications communicate with the Skoda server.') from connection_error
|
|
1446
|
+
except requests.exceptions.ChunkedEncodingError as chunked_encoding_error:
|
|
1447
|
+
raise SetterError(f'Error: {chunked_encoding_error}') from chunked_encoding_error
|
|
1448
|
+
except requests.exceptions.ReadTimeout as timeout_error:
|
|
1449
|
+
raise SetterError(f'Timeout during read: {timeout_error}') from timeout_error
|
|
1450
|
+
except requests.exceptions.RetryError as retry_error:
|
|
1451
|
+
raise SetterError(f'Retrying failed: {retry_error}') from retry_error
|
|
1362
1452
|
return at_unlock_value
|
|
1363
1453
|
|
|
1364
1454
|
def __on_air_conditioning_window_heating_change(self, window_heating_attribute: BooleanAttribute, window_heating_value: bool) -> bool:
|
|
@@ -1374,10 +1464,20 @@ class Connector(BaseConnector):
|
|
|
1374
1464
|
setting_dict['windowHeatingEnabled'] = window_heating_value
|
|
1375
1465
|
|
|
1376
1466
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/settings/ac-at-unlock'
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1467
|
+
try:
|
|
1468
|
+
settings_response: requests.Response = self.session.post(url, data=json.dumps(setting_dict), allow_redirects=True)
|
|
1469
|
+
if settings_response.status_code != requests.codes['accepted']:
|
|
1470
|
+
LOG.error('Could not set air conditioning window heating (%s)', settings_response.status_code)
|
|
1471
|
+
raise SetterError(f'Could not set value ({settings_response.status_code})')
|
|
1472
|
+
except requests.exceptions.ConnectionError as connection_error:
|
|
1473
|
+
raise SetterError(f'Connection error: {connection_error}.'
|
|
1474
|
+
' If this happens frequently, please check if other applications communicate with the Skoda server.') from connection_error
|
|
1475
|
+
except requests.exceptions.ChunkedEncodingError as chunked_encoding_error:
|
|
1476
|
+
raise SetterError(f'Error: {chunked_encoding_error}') from chunked_encoding_error
|
|
1477
|
+
except requests.exceptions.ReadTimeout as timeout_error:
|
|
1478
|
+
raise SetterError(f'Timeout during read: {timeout_error}') from timeout_error
|
|
1479
|
+
except requests.exceptions.RetryError as retry_error:
|
|
1480
|
+
raise SetterError(f'Retrying failed: {retry_error}') from retry_error
|
|
1381
1481
|
return window_heating_value
|
|
1382
1482
|
|
|
1383
1483
|
def __on_air_conditioning_start_stop(self, start_stop_command: ClimatizationStartStopCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
|
@@ -1394,53 +1494,63 @@ class Connector(BaseConnector):
|
|
|
1394
1494
|
if 'command' not in command_arguments:
|
|
1395
1495
|
raise CommandError('Command argument missing')
|
|
1396
1496
|
command_dict = {}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
if
|
|
1405
|
-
|
|
1406
|
-
|
|
1497
|
+
try:
|
|
1498
|
+
if command_arguments['command'] == ClimatizationStartStopCommand.Command.START:
|
|
1499
|
+
command_dict['heaterSource'] = 'ELECTRIC'
|
|
1500
|
+
command_dict['targetTemperature'] = {}
|
|
1501
|
+
if 'target_temperature' in command_arguments:
|
|
1502
|
+
# Round target temperature to nearest 0.5
|
|
1503
|
+
command_dict['targetTemperature']['temperatureValue'] = round(command_arguments['target_temperature'] * 2) / 2
|
|
1504
|
+
if 'target_temperature_unit' in command_arguments:
|
|
1505
|
+
if not isinstance(command_arguments['target_temperature_unit'], Temperature):
|
|
1506
|
+
raise CommandError('Temperature unit is not of type Temperature')
|
|
1507
|
+
if command_arguments['target_temperature_unit'] == Temperature.C:
|
|
1508
|
+
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1509
|
+
elif command_arguments['target_temperature_unit'] == Temperature.F:
|
|
1510
|
+
command_dict['targetTemperature']['unitInCar'] = 'FAHRENHEIT'
|
|
1511
|
+
elif command_arguments['target_temperature_unit'] == Temperature.K:
|
|
1512
|
+
command_dict['targetTemperature']['unitInCar'] = 'KELVIN'
|
|
1513
|
+
else:
|
|
1514
|
+
raise CommandError(f'Unknown temperature unit {command_arguments["target_temperature_unit"]}')
|
|
1515
|
+
else:
|
|
1516
|
+
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1517
|
+
elif start_stop_command.parent is not None and (climatization := start_stop_command.parent.parent) is not None \
|
|
1518
|
+
and isinstance(climatization, Climatization) and climatization.settings is not None \
|
|
1519
|
+
and climatization.settings.target_temperature is not None and climatization.settings.target_temperature.enabled \
|
|
1520
|
+
and climatization.settings.target_temperature.value is not None: # pylint: disable=too-many-boolean-expressions
|
|
1521
|
+
# Round target temperature to nearest 0.5
|
|
1522
|
+
command_dict['targetTemperature']['temperatureValue'] = round(climatization.settings.target_temperature.value * 2) / 2
|
|
1523
|
+
if climatization.settings.target_temperature.unit == Temperature.C:
|
|
1407
1524
|
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1408
|
-
elif
|
|
1525
|
+
elif climatization.settings.target_temperature.unit == Temperature.F:
|
|
1409
1526
|
command_dict['targetTemperature']['unitInCar'] = 'FAHRENHEIT'
|
|
1410
|
-
elif
|
|
1527
|
+
elif climatization.settings.target_temperature.unit == Temperature.K:
|
|
1411
1528
|
command_dict['targetTemperature']['unitInCar'] = 'KELVIN'
|
|
1412
1529
|
else:
|
|
1413
|
-
raise CommandError(f'Unknown temperature unit {
|
|
1530
|
+
raise CommandError(f'Unknown temperature unit {climatization.settings.target_temperature.unit}')
|
|
1414
1531
|
else:
|
|
1532
|
+
command_dict['targetTemperature']['temperatureValue'] = 25.0
|
|
1415
1533
|
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
command_dict['targetTemperature']['temperatureValue'] = round(climatization.settings.target_temperature.value * 2) / 2
|
|
1422
|
-
if climatization.settings.target_temperature.unit == Temperature.C:
|
|
1423
|
-
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1424
|
-
elif climatization.settings.target_temperature.unit == Temperature.F:
|
|
1425
|
-
command_dict['targetTemperature']['unitInCar'] = 'FAHRENHEIT'
|
|
1426
|
-
elif climatization.settings.target_temperature.unit == Temperature.K:
|
|
1427
|
-
command_dict['targetTemperature']['unitInCar'] = 'KELVIN'
|
|
1428
|
-
else:
|
|
1429
|
-
raise CommandError(f'Unknown temperature unit {climatization.settings.target_temperature.unit}')
|
|
1534
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/start'
|
|
1535
|
+
command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
|
1536
|
+
elif command_arguments['command'] == ClimatizationStartStopCommand.Command.STOP:
|
|
1537
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/stop'
|
|
1538
|
+
command_response: requests.Response = self.session.post(url, allow_redirects=True)
|
|
1430
1539
|
else:
|
|
1431
|
-
|
|
1432
|
-
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1433
|
-
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/start'
|
|
1434
|
-
command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
|
1435
|
-
elif command_arguments['command'] == ClimatizationStartStopCommand.Command.STOP:
|
|
1436
|
-
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/stop'
|
|
1437
|
-
command_response: requests.Response = self.session.post(url, allow_redirects=True)
|
|
1438
|
-
else:
|
|
1439
|
-
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
|
1540
|
+
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
|
1440
1541
|
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1542
|
+
if command_response.status_code != requests.codes['accepted']:
|
|
1543
|
+
LOG.error('Could not start/stop air conditioning (%s: %s)', command_response.status_code, command_response.text)
|
|
1544
|
+
raise CommandError(f'Could not start/stop air conditioning ({command_response.status_code}: {command_response.text})')
|
|
1545
|
+
except requests.exceptions.ConnectionError as connection_error:
|
|
1546
|
+
raise CommandError(f'Connection error: {connection_error}.'
|
|
1547
|
+
' If this happens frequently, please check if other applications communicate with the Skoda server.') from connection_error
|
|
1548
|
+
except requests.exceptions.ChunkedEncodingError as chunked_encoding_error:
|
|
1549
|
+
raise CommandError(f'Error: {chunked_encoding_error}') from chunked_encoding_error
|
|
1550
|
+
except requests.exceptions.ReadTimeout as timeout_error:
|
|
1551
|
+
raise CommandError(f'Timeout during read: {timeout_error}') from timeout_error
|
|
1552
|
+
except requests.exceptions.RetryError as retry_error:
|
|
1553
|
+
raise CommandError(f'Retrying failed: {retry_error}') from retry_error
|
|
1444
1554
|
return command_arguments
|
|
1445
1555
|
|
|
1446
1556
|
def __on_charging_start_stop(self, start_stop_command: ChargingStartStopCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
|
@@ -1456,18 +1566,29 @@ class Connector(BaseConnector):
|
|
|
1456
1566
|
raise CommandError('VIN in object hierarchy missing')
|
|
1457
1567
|
if 'command' not in command_arguments:
|
|
1458
1568
|
raise CommandError('Command argument missing')
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1569
|
+
try:
|
|
1570
|
+
if command_arguments['command'] == ChargingStartStopCommand.Command.START:
|
|
1571
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}/start'
|
|
1572
|
+
command_response: requests.Response = self.session.post(url, allow_redirects=True)
|
|
1573
|
+
elif command_arguments['command'] == ChargingStartStopCommand.Command.STOP:
|
|
1574
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}/stop'
|
|
1575
|
+
|
|
1576
|
+
command_response: requests.Response = self.session.post(url, allow_redirects=True)
|
|
1577
|
+
else:
|
|
1578
|
+
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
|
1467
1579
|
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1580
|
+
if command_response.status_code != requests.codes['accepted']:
|
|
1581
|
+
LOG.error('Could not start/stop charging (%s: %s)', command_response.status_code, command_response.text)
|
|
1582
|
+
raise CommandError(f'Could not start/stop charging ({command_response.status_code}: {command_response.text})')
|
|
1583
|
+
except requests.exceptions.ConnectionError as connection_error:
|
|
1584
|
+
raise CommandError(f'Connection error: {connection_error}.'
|
|
1585
|
+
' If this happens frequently, please check if other applications communicate with the Skoda server.') from connection_error
|
|
1586
|
+
except requests.exceptions.ChunkedEncodingError as chunked_encoding_error:
|
|
1587
|
+
raise CommandError(f'Error: {chunked_encoding_error}') from chunked_encoding_error
|
|
1588
|
+
except requests.exceptions.ReadTimeout as timeout_error:
|
|
1589
|
+
raise CommandError(f'Timeout during read: {timeout_error}') from timeout_error
|
|
1590
|
+
except requests.exceptions.RetryError as retry_error:
|
|
1591
|
+
raise CommandError(f'Retrying failed: {retry_error}') from retry_error
|
|
1471
1592
|
return command_arguments
|
|
1472
1593
|
|
|
1473
1594
|
def __on_honk_flash(self, honk_flash_command: HonkAndFlashCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
|
@@ -1497,10 +1618,20 @@ class Connector(BaseConnector):
|
|
|
1497
1618
|
command_dict['vehiclePosition']['longitude'] = vehicle.position.longitude.value
|
|
1498
1619
|
|
|
1499
1620
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/vehicle-access/{vin}/honk-and-flash'
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1621
|
+
try:
|
|
1622
|
+
command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
|
1623
|
+
if command_response.status_code != requests.codes['accepted']:
|
|
1624
|
+
LOG.error('Could not execute honk or flash command (%s: %s)', command_response.status_code, command_response.text)
|
|
1625
|
+
raise CommandError(f'Could not execute honk or flash command ({command_response.status_code}: {command_response.text})')
|
|
1626
|
+
except requests.exceptions.ConnectionError as connection_error:
|
|
1627
|
+
raise CommandError(f'Connection error: {connection_error}.'
|
|
1628
|
+
' If this happens frequently, please check if other applications communicate with the Skoda server.') from connection_error
|
|
1629
|
+
except requests.exceptions.ChunkedEncodingError as chunked_encoding_error:
|
|
1630
|
+
raise SetterError(f'Error: {chunked_encoding_error}') from chunked_encoding_error
|
|
1631
|
+
except requests.exceptions.ReadTimeout as timeout_error:
|
|
1632
|
+
raise CommandError(f'Timeout during read: {timeout_error}') from timeout_error
|
|
1633
|
+
except requests.exceptions.RetryError as retry_error:
|
|
1634
|
+
raise CommandError(f'Retrying failed: {retry_error}') from retry_error
|
|
1504
1635
|
else:
|
|
1505
1636
|
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
|
1506
1637
|
return command_arguments
|
|
@@ -1531,10 +1662,20 @@ class Connector(BaseConnector):
|
|
|
1531
1662
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/vehicle-access/{vin}/unlock'
|
|
1532
1663
|
else:
|
|
1533
1664
|
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1665
|
+
try:
|
|
1666
|
+
command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
|
1667
|
+
if command_response.status_code != requests.codes['accepted']:
|
|
1668
|
+
LOG.error('Could not execute locking command (%s: %s)', command_response.status_code, command_response.text)
|
|
1669
|
+
raise CommandError(f'Could not execute locking command ({command_response.status_code}: {command_response.text})')
|
|
1670
|
+
except requests.exceptions.ConnectionError as connection_error:
|
|
1671
|
+
raise CommandError(f'Connection error: {connection_error}.'
|
|
1672
|
+
' If this happens frequently, please check if other applications communicate with the Skoda server.') from connection_error
|
|
1673
|
+
except requests.exceptions.ChunkedEncodingError as chunked_encoding_error:
|
|
1674
|
+
raise CommandError(f'Error: {chunked_encoding_error}') from chunked_encoding_error
|
|
1675
|
+
except requests.exceptions.ReadTimeout as timeout_error:
|
|
1676
|
+
raise CommandError(f'Timeout during read: {timeout_error}') from timeout_error
|
|
1677
|
+
except requests.exceptions.RetryError as retry_error:
|
|
1678
|
+
raise CommandError(f'Retrying failed: {retry_error}') from retry_error
|
|
1538
1679
|
return command_arguments
|
|
1539
1680
|
|
|
1540
1681
|
def __on_wake_sleep(self, wake_sleep_command: WakeSleepCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
|
@@ -1553,10 +1694,20 @@ class Connector(BaseConnector):
|
|
|
1553
1694
|
if command_arguments['command'] == WakeSleepCommand.Command.WAKE:
|
|
1554
1695
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/vehicle-wakeup/{vin}?applyRequestLimiter=true'
|
|
1555
1696
|
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1697
|
+
try:
|
|
1698
|
+
command_response: requests.Response = self.session.post(url, data='{}', allow_redirects=True)
|
|
1699
|
+
if command_response.status_code != requests.codes['accepted']:
|
|
1700
|
+
LOG.error('Could not execute wake command (%s: %s)', command_response.status_code, command_response.text)
|
|
1701
|
+
raise CommandError(f'Could not execute wake command ({command_response.status_code}: {command_response.text})')
|
|
1702
|
+
except requests.exceptions.ConnectionError as connection_error:
|
|
1703
|
+
raise CommandError(f'Connection error: {connection_error}.'
|
|
1704
|
+
' If this happens frequently, please check if other applications communicate with the Skoda server.') from connection_error
|
|
1705
|
+
except requests.exceptions.ChunkedEncodingError as chunked_encoding_error:
|
|
1706
|
+
raise CommandError(f'Error: {chunked_encoding_error}') from chunked_encoding_error
|
|
1707
|
+
except requests.exceptions.ReadTimeout as timeout_error:
|
|
1708
|
+
raise CommandError(f'Timeout during read: {timeout_error}') from timeout_error
|
|
1709
|
+
except requests.exceptions.RetryError as retry_error:
|
|
1710
|
+
raise CommandError(f'Retrying failed: {retry_error}') from retry_error
|
|
1560
1711
|
elif command_arguments['command'] == WakeSleepCommand.Command.SLEEP:
|
|
1561
1712
|
raise CommandError('Sleep command not supported by vehicle. Vehicle will put itself to sleep')
|
|
1562
1713
|
else:
|
|
@@ -1583,10 +1734,20 @@ class Connector(BaseConnector):
|
|
|
1583
1734
|
url = 'https://mysmob.api.connect.skoda-auto.cz/api/v1/spin/verify'
|
|
1584
1735
|
else:
|
|
1585
1736
|
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1737
|
+
try:
|
|
1738
|
+
command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
|
1739
|
+
if command_response.status_code != requests.codes['ok']:
|
|
1740
|
+
LOG.error('Could not execute spin command (%s: %s)', command_response.status_code, command_response.text)
|
|
1741
|
+
raise CommandError(f'Could not execute spin command ({command_response.status_code}: {command_response.text})')
|
|
1742
|
+
else:
|
|
1743
|
+
LOG.info('Spin verify command executed successfully')
|
|
1744
|
+
except requests.exceptions.ConnectionError as connection_error:
|
|
1745
|
+
raise CommandError(f'Connection error: {connection_error}.'
|
|
1746
|
+
' If this happens frequently, please check if other applications communicate with the Skoda server.') from connection_error
|
|
1747
|
+
except requests.exceptions.ChunkedEncodingError as chunked_encoding_error:
|
|
1748
|
+
raise CommandError(f'Error: {chunked_encoding_error}') from chunked_encoding_error
|
|
1749
|
+
except requests.exceptions.ReadTimeout as timeout_error:
|
|
1750
|
+
raise CommandError(f'Timeout during read: {timeout_error}') from timeout_error
|
|
1751
|
+
except requests.exceptions.RetryError as retry_error:
|
|
1752
|
+
raise CommandError(f'Retrying failed: {retry_error}') from retry_error
|
|
1592
1753
|
return command_arguments
|
|
@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING
|
|
|
4
4
|
|
|
5
5
|
from carconnectivity.vehicle import GenericVehicle, ElectricVehicle, CombustionVehicle, HybridVehicle
|
|
6
6
|
from carconnectivity.charging import Charging
|
|
7
|
+
from carconnectivity.attributes import BooleanAttribute
|
|
7
8
|
|
|
8
9
|
from carconnectivity_connectors.skoda.capability import Capabilities
|
|
9
10
|
from carconnectivity_connectors.skoda.charging import SkodaCharging
|
|
@@ -32,6 +33,8 @@ class SkodaVehicle(GenericVehicle): # pylint: disable=too-many-instance-attribu
|
|
|
32
33
|
super().__init__(origin=origin)
|
|
33
34
|
self.capabilities: Capabilities = origin.capabilities
|
|
34
35
|
self.capabilities.parent = self
|
|
36
|
+
self.in_motion: BooleanAttribute = origin.in_motion
|
|
37
|
+
self.in_motion.parent = self
|
|
35
38
|
if SUPPORT_IMAGES:
|
|
36
39
|
self._car_images = origin._car_images
|
|
37
40
|
|
|
@@ -39,6 +42,7 @@ class SkodaVehicle(GenericVehicle): # pylint: disable=too-many-instance-attribu
|
|
|
39
42
|
super().__init__(vin=vin, garage=garage, managing_connector=managing_connector)
|
|
40
43
|
self.climatization = SkodaClimatization(vehicle=self, origin=self.climatization)
|
|
41
44
|
self.capabilities = Capabilities(vehicle=self)
|
|
45
|
+
self.in_motion = BooleanAttribute(name='in_motion', parent=self, tags={'connector_custom'})
|
|
42
46
|
if SUPPORT_IMAGES:
|
|
43
47
|
self._car_images: Dict[str, Image.Image] = {}
|
|
44
48
|
self.manufacturer._set_value(value='Škoda') # pylint: disable=protected-access
|
|
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.4a2 → carconnectivity_connector_skoda-0.4a4}/CHANGELOG.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{carconnectivity_connector_skoda-0.4a2 → carconnectivity_connector_skoda-0.4a4}/doc/Config.md
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|