carconnectivity-connector-skoda 0.1a18__tar.gz → 0.1a20__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.1a18 → carconnectivity_connector_skoda-0.1a20}/Makefile +1 -1
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/PKG-INFO +1 -1
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connector_skoda.egg-info/PKG-INFO +1 -1
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/_version.py +1 -1
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/charging.py +2 -2
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/connector.py +201 -10
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/error.py +1 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/mqtt_client.py +12 -6
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/vehicle.py +1 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/.flake8 +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/.github/dependabot.yml +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/.github/workflows/build.yml +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/.github/workflows/build_and_publish.yml +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/.github/workflows/codeql-analysis.yml +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/.gitignore +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/LICENSE +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/README.md +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/doc/Config.md +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/pyproject.toml +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/setup.cfg +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/setup_requirements.txt +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connector_skoda.egg-info/SOURCES.txt +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connector_skoda.egg-info/dependency_links.txt +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connector_skoda.egg-info/requires.txt +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connector_skoda.egg-info/top_level.txt +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/__init__.py +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/auth/__init__.py +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/auth/auth_util.py +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/auth/helpers/blacklist_retry.py +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/auth/my_skoda_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/auth/openid_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/auth/session_manager.py +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/auth/skoda_web_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/capability.py +0 -0
- {carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/src/carconnectivity_connectors/skoda/climatization.py +0 -0
|
@@ -6,7 +6,7 @@ test:
|
|
|
6
6
|
|
|
7
7
|
lint:
|
|
8
8
|
@echo "\n${BLUE}Running Pylint against source and test files...${NC}\n"
|
|
9
|
-
@pylint ./
|
|
9
|
+
@pylint ./src
|
|
10
10
|
@echo "\n${BLUE}Running Flake8 against source and test files...${NC}\n"
|
|
11
11
|
@flake8
|
|
12
12
|
@echo "\n${BLUE}Running Bandit against source files...${NC}\n"
|
|
@@ -69,7 +69,7 @@ class SkodaCharging(Charging): # pylint: disable=too-many-instance-attributes
|
|
|
69
69
|
CONNECT_CABLE = 'connectCable'
|
|
70
70
|
READY_FOR_CHARGING = 'readyForCharging'
|
|
71
71
|
NOT_READY_FOR_CHARGING = 'notReadyForCharging'
|
|
72
|
-
|
|
72
|
+
CONSERVING = 'conserving'
|
|
73
73
|
CHARGE_PURPOSE_REACHED_NOT_CONSERVATION_CHARGING = 'chargePurposeReachedAndNotConservationCharging'
|
|
74
74
|
CHARGE_PURPOSE_REACHED_CONSERVATION = 'chargePurposeReachedAndConservation'
|
|
75
75
|
CHARGING = 'charging'
|
|
@@ -126,7 +126,7 @@ mapping_skoda_charging_state: Dict[SkodaCharging.SkodaChargingState, Charging.Ch
|
|
|
126
126
|
SkodaCharging.SkodaChargingState.CONNECT_CABLE: Charging.ChargingState.OFF,
|
|
127
127
|
SkodaCharging.SkodaChargingState.READY_FOR_CHARGING: Charging.ChargingState.READY_FOR_CHARGING,
|
|
128
128
|
SkodaCharging.SkodaChargingState.NOT_READY_FOR_CHARGING: Charging.ChargingState.OFF,
|
|
129
|
-
SkodaCharging.SkodaChargingState.
|
|
129
|
+
SkodaCharging.SkodaChargingState.CONSERVING: Charging.ChargingState.CONSERVATION,
|
|
130
130
|
SkodaCharging.SkodaChargingState.CHARGE_PURPOSE_REACHED_NOT_CONSERVATION_CHARGING: Charging.ChargingState.READY_FOR_CHARGING,
|
|
131
131
|
SkodaCharging.SkodaChargingState.CHARGE_PURPOSE_REACHED_CONSERVATION: Charging.ChargingState.CONSERVATION,
|
|
132
132
|
SkodaCharging.SkodaChargingState.CHARGING: Charging.ChargingState.CHARGING,
|
|
@@ -7,23 +7,27 @@ import os
|
|
|
7
7
|
import logging
|
|
8
8
|
import netrc
|
|
9
9
|
from datetime import datetime, timedelta, timezone
|
|
10
|
+
import json
|
|
11
|
+
|
|
10
12
|
import requests
|
|
11
13
|
|
|
14
|
+
|
|
12
15
|
from carconnectivity.garage import Garage
|
|
13
16
|
from carconnectivity.vehicle import GenericVehicle
|
|
14
17
|
from carconnectivity.errors import AuthenticationError, TooManyRequestsError, RetrievalError, APIError, APICompatibilityError, \
|
|
15
|
-
TemporaryAuthenticationError, ConfigurationError
|
|
18
|
+
TemporaryAuthenticationError, ConfigurationError, SetterError
|
|
16
19
|
from carconnectivity.util import robust_time_parse, log_extra_keys, config_remove_credentials
|
|
17
20
|
from carconnectivity.units import Length, Speed, Power, Temperature
|
|
18
21
|
from carconnectivity.doors import Doors
|
|
19
22
|
from carconnectivity.windows import Windows
|
|
20
23
|
from carconnectivity.lights import Lights
|
|
21
24
|
from carconnectivity.drive import GenericDrive, ElectricDrive, CombustionDrive
|
|
22
|
-
from carconnectivity.attributes import BooleanAttribute, DurationAttribute
|
|
25
|
+
from carconnectivity.attributes import BooleanAttribute, DurationAttribute, TemperatureAttribute
|
|
23
26
|
from carconnectivity.charging import Charging
|
|
24
27
|
from carconnectivity.position import Position
|
|
25
28
|
from carconnectivity.climatization import Climatization
|
|
26
29
|
from carconnectivity.charging_connector import ChargingConnector
|
|
30
|
+
from carconnectivity.command_impl import ClimatizationStartStopCommand, ChargingStartStopCommand
|
|
27
31
|
|
|
28
32
|
from carconnectivity_connectors.base.connector import BaseConnector
|
|
29
33
|
from carconnectivity_connectors.skoda.auth.session_manager import SessionManager, SessionUser, Service
|
|
@@ -37,7 +41,7 @@ from carconnectivity_connectors.skoda._version import __version__
|
|
|
37
41
|
from carconnectivity_connectors.skoda.mqtt_client import SkodaMQTTClient
|
|
38
42
|
|
|
39
43
|
if TYPE_CHECKING:
|
|
40
|
-
from typing import Dict, List, Optional, Any, Set
|
|
44
|
+
from typing import Dict, List, Optional, Any, Set, Union
|
|
41
45
|
|
|
42
46
|
from carconnectivity.carconnectivity import CarConnectivity
|
|
43
47
|
|
|
@@ -71,20 +75,20 @@ class Connector(BaseConnector):
|
|
|
71
75
|
# Configure logging
|
|
72
76
|
if 'log_level' in config and config['log_level'] is not None:
|
|
73
77
|
config['log_level'] = config['log_level'].upper()
|
|
74
|
-
if config['log_level'] in logging.
|
|
78
|
+
if config['log_level'] in logging._nameToLevel:
|
|
75
79
|
LOG.setLevel(config['log_level'])
|
|
76
80
|
self.log_level._set_value(config['log_level']) # pylint: disable=protected-access
|
|
77
81
|
logging.getLogger('requests').setLevel(config['log_level'])
|
|
78
82
|
logging.getLogger('urllib3').setLevel(config['log_level'])
|
|
79
83
|
logging.getLogger('oauthlib').setLevel(config['log_level'])
|
|
80
84
|
else:
|
|
81
|
-
raise ConfigurationError(f'Invalid log level: "{config["log_level"]}" not in {list(logging.
|
|
85
|
+
raise ConfigurationError(f'Invalid log level: "{config["log_level"]}" not in {list(logging._nameToLevel.keys())}')
|
|
82
86
|
if 'api_log_level' in config and config['api_log_level'] is not None:
|
|
83
87
|
config['api_log_level'] = config['api_log_level'].upper()
|
|
84
|
-
if config['api_log_level'] in logging.
|
|
88
|
+
if config['api_log_level'] in logging._nameToLevel:
|
|
85
89
|
LOG_API.setLevel(config['api_log_level'])
|
|
86
90
|
else:
|
|
87
|
-
raise ConfigurationError(f'Invalid log level: "{config["log_level"]}" not in {list(logging.
|
|
91
|
+
raise ConfigurationError(f'Invalid log level: "{config["log_level"]}" not in {list(logging._nameToLevel.keys())}')
|
|
88
92
|
LOG.info("Loading skoda connector with config %s", config_remove_credentials(self.config))
|
|
89
93
|
|
|
90
94
|
username: Optional[str] = None
|
|
@@ -171,10 +175,10 @@ class Connector(BaseConnector):
|
|
|
171
175
|
self.update_vehicles()
|
|
172
176
|
self.last_update._set_value(value=datetime.now(tz=timezone.utc)) # pylint: disable=protected-access
|
|
173
177
|
if self.interval.value is not None:
|
|
174
|
-
interval:
|
|
178
|
+
interval: float = self.interval.value.total_seconds()
|
|
175
179
|
except Exception:
|
|
176
180
|
if self.interval.value is not None:
|
|
177
|
-
interval:
|
|
181
|
+
interval: float = self.interval.value.total_seconds()
|
|
178
182
|
raise
|
|
179
183
|
except TooManyRequestsError as err:
|
|
180
184
|
LOG.error('Retrieval error during update. Too many requests from your account (%s). Will try again after 15 minutes', str(err))
|
|
@@ -283,7 +287,12 @@ class Connector(BaseConnector):
|
|
|
283
287
|
else:
|
|
284
288
|
vehicle.license_plate._set_value(None) # pylint: disable=protected-access
|
|
285
289
|
|
|
286
|
-
|
|
290
|
+
if 'name' in vehicle_dict and vehicle_dict['name'] is not None:
|
|
291
|
+
vehicle.name._set_value(vehicle_dict['name']) # pylint: disable=protected-access
|
|
292
|
+
else:
|
|
293
|
+
vehicle.name._set_value(None) # pylint: disable=protected-access
|
|
294
|
+
|
|
295
|
+
log_extra_keys(LOG_API, 'vehicles', vehicle_dict, {'vin', 'licensePlate', 'name'})
|
|
287
296
|
|
|
288
297
|
vehicle = self.fetch_vehicle_details(vehicle)
|
|
289
298
|
else:
|
|
@@ -339,6 +348,10 @@ class Connector(BaseConnector):
|
|
|
339
348
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}'
|
|
340
349
|
data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
|
341
350
|
if data is not None:
|
|
351
|
+
start_stop_command: ChargingStartStopCommand = ChargingStartStopCommand(parent=vehicle.charging.commands)
|
|
352
|
+
start_stop_command._add_on_set_hook(self.__on_charging_start_stop) # pylint: disable=protected-access
|
|
353
|
+
start_stop_command.enabled = True
|
|
354
|
+
vehicle.charging.commands.add_command(start_stop_command)
|
|
342
355
|
if 'carCapturedTimestamp' in data and data['carCapturedTimestamp'] is not None:
|
|
343
356
|
captured_at: datetime = robust_time_parse(data['carCapturedTimestamp'])
|
|
344
357
|
else:
|
|
@@ -604,6 +617,13 @@ class Connector(BaseConnector):
|
|
|
604
617
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}'
|
|
605
618
|
data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
|
606
619
|
if data is not None:
|
|
620
|
+
if vehicle.climatization is not None and vehicle.climatization.commands is not None \
|
|
621
|
+
and not vehicle.climatization.commands.contains_command('start-stop'):
|
|
622
|
+
start_stop_command = ClimatizationStartStopCommand(parent=vehicle.climatization.commands)
|
|
623
|
+
start_stop_command._add_on_set_hook(self.__on_air_conditioning_start_stop) # pylint: disable=protected-access
|
|
624
|
+
start_stop_command.enabled = True
|
|
625
|
+
vehicle.climatization.commands.add_command(start_stop_command)
|
|
626
|
+
|
|
607
627
|
if 'carCapturedTimestamp' in data and data['carCapturedTimestamp'] is not None:
|
|
608
628
|
captured_at: datetime = robust_time_parse(data['carCapturedTimestamp'])
|
|
609
629
|
else:
|
|
@@ -626,6 +646,9 @@ class Connector(BaseConnector):
|
|
|
626
646
|
else:
|
|
627
647
|
vehicle.climatization.estimated_date_reached._set_value(value=None, measured=captured_at) # pylint: disable=protected-access
|
|
628
648
|
if 'targetTemperature' in data and data['targetTemperature'] is not None:
|
|
649
|
+
# pylint: disable-next=protected-access
|
|
650
|
+
vehicle.climatization.settings.target_temperature._add_on_set_hook(self.__on_air_conditioning_target_temperature_change)
|
|
651
|
+
vehicle.climatization.settings.target_temperature._is_changeable = True # pylint: disable=protected-access
|
|
629
652
|
unit: Temperature = Temperature.UNKNOWN
|
|
630
653
|
if 'unitInCar' in data['targetTemperature'] and data['targetTemperature']['unitInCar'] is not None:
|
|
631
654
|
if data['targetTemperature']['unitInCar'] == 'CELSIUS':
|
|
@@ -679,6 +702,9 @@ class Connector(BaseConnector):
|
|
|
679
702
|
vehicle.outside_temperature._set_value(value=None, measured=None, unit=Temperature.UNKNOWN) # pylint: disable=protected-access
|
|
680
703
|
if 'airConditioningAtUnlock' in data and data['airConditioningAtUnlock'] is not None:
|
|
681
704
|
if vehicle.climatization is not None and vehicle.climatization.settings is not None:
|
|
705
|
+
# pylint: disable-next=protected-access
|
|
706
|
+
vehicle.climatization.settings.climatization_at_unlock._add_on_set_hook(self.__on_air_conditioning_at_unlock_change)
|
|
707
|
+
vehicle.climatization.settings.climatization_at_unlock._is_changeable = True # pylint: disable=protected-access
|
|
682
708
|
if data['airConditioningAtUnlock'] is True:
|
|
683
709
|
# pylint: disable-next=protected-access
|
|
684
710
|
vehicle.climatization.settings.climatization_at_unlock._set_value(True, measured=captured_at)
|
|
@@ -709,6 +735,9 @@ class Connector(BaseConnector):
|
|
|
709
735
|
vehicle.specification.steering_wheel_position._set_value(None, measured=captured_at)
|
|
710
736
|
if 'windowHeatingEnabled' in data and data['windowHeatingEnabled'] is not None:
|
|
711
737
|
if vehicle.climatization is not None and vehicle.climatization.settings is not None:
|
|
738
|
+
# pylint: disable-next=protected-access
|
|
739
|
+
vehicle.climatization.settings.window_heating._add_on_set_hook(self.__on_air_conditioning_window_heating_change)
|
|
740
|
+
vehicle.climatization.settings.window_heating._is_changeable = True # pylint: disable=protected-access
|
|
712
741
|
if data['windowHeatingEnabled'] is True:
|
|
713
742
|
# pylint: disable-next=protected-access
|
|
714
743
|
vehicle.climatization.settings.window_heating._set_value(True, measured=captured_at)
|
|
@@ -1295,3 +1324,165 @@ class Connector(BaseConnector):
|
|
|
1295
1324
|
|
|
1296
1325
|
def get_version(self) -> str:
|
|
1297
1326
|
return __version__
|
|
1327
|
+
|
|
1328
|
+
def __on_air_conditioning_target_temperature_change(self, temperature_attribute: TemperatureAttribute, target_temperature: float) -> float:
|
|
1329
|
+
"""
|
|
1330
|
+
Callback for the climatization target temperature change.
|
|
1331
|
+
|
|
1332
|
+
Args:
|
|
1333
|
+
temperature_attribute (TemperatureAttribute): The temperature attribute that changed.
|
|
1334
|
+
target_temperature (float): The new target temperature.
|
|
1335
|
+
"""
|
|
1336
|
+
if temperature_attribute.parent is None or temperature_attribute.parent.parent is None \
|
|
1337
|
+
or temperature_attribute.parent.parent.parent is None or not isinstance(temperature_attribute.parent.parent.parent, SkodaVehicle):
|
|
1338
|
+
raise SetterError('Object hierarchy is not as expected')
|
|
1339
|
+
vehicle: SkodaVehicle = temperature_attribute.parent.parent.parent
|
|
1340
|
+
vin: Optional[str] = vehicle.vin.value
|
|
1341
|
+
if vin is None:
|
|
1342
|
+
raise SetterError('VIN in object hierarchy missing')
|
|
1343
|
+
setting_dict = {}
|
|
1344
|
+
# Round target temperature to nearest 0.5
|
|
1345
|
+
setting_dict['temperatureValue'] = round(target_temperature * 2) / 2
|
|
1346
|
+
if temperature_attribute.unit == Temperature.C:
|
|
1347
|
+
setting_dict['unitInCar'] = 'CELSIUS'
|
|
1348
|
+
elif temperature_attribute.unit == Temperature.F:
|
|
1349
|
+
setting_dict['unitInCar'] = 'FAHRENHEIT'
|
|
1350
|
+
elif temperature_attribute.unit == Temperature.K:
|
|
1351
|
+
setting_dict['unitInCar'] = 'KELVIN'
|
|
1352
|
+
else:
|
|
1353
|
+
raise SetterError(f'Unknown temperature unit {temperature_attribute.unit}')
|
|
1354
|
+
|
|
1355
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/settings/target-temperature'
|
|
1356
|
+
settings_response: requests.Response = self.session.post(url, data=json.dumps(setting_dict), allow_redirects=True)
|
|
1357
|
+
if settings_response.status_code != requests.codes['accepted']:
|
|
1358
|
+
LOG.error('Could not set target temperature (%s)', settings_response.status_code)
|
|
1359
|
+
raise SetterError(f'Could not set value ({settings_response.status_code})')
|
|
1360
|
+
return target_temperature
|
|
1361
|
+
|
|
1362
|
+
def __on_air_conditioning_at_unlock_change(self, at_unlock_attribute: BooleanAttribute, at_unlock_value: bool) -> bool:
|
|
1363
|
+
if at_unlock_attribute.parent is None or at_unlock_attribute.parent.parent is None \
|
|
1364
|
+
or at_unlock_attribute.parent.parent.parent is None or not isinstance(at_unlock_attribute.parent.parent.parent, SkodaVehicle):
|
|
1365
|
+
raise SetterError('Object hierarchy is not as expected')
|
|
1366
|
+
vehicle: SkodaVehicle = at_unlock_attribute.parent.parent.parent
|
|
1367
|
+
vin: Optional[str] = vehicle.vin.value
|
|
1368
|
+
if vin is None:
|
|
1369
|
+
raise SetterError('VIN in object hierarchy missing')
|
|
1370
|
+
setting_dict = {}
|
|
1371
|
+
# Round target temperature to nearest 0.5
|
|
1372
|
+
setting_dict['airConditioningAtUnlockEnabled'] = at_unlock_value
|
|
1373
|
+
|
|
1374
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/settings/ac-at-unlock'
|
|
1375
|
+
settings_response: requests.Response = self.session.post(url, data=json.dumps(setting_dict), allow_redirects=True)
|
|
1376
|
+
if settings_response.status_code != requests.codes['accepted']:
|
|
1377
|
+
LOG.error('Could not set air conditioning at unlock (%s)', settings_response.status_code)
|
|
1378
|
+
raise SetterError(f'Could not set value ({settings_response.status_code})')
|
|
1379
|
+
return at_unlock_value
|
|
1380
|
+
|
|
1381
|
+
def __on_air_conditioning_window_heating_change(self, window_heating_attribute: BooleanAttribute, window_heating_value: bool) -> bool:
|
|
1382
|
+
if window_heating_attribute.parent is None or window_heating_attribute.parent.parent is None \
|
|
1383
|
+
or window_heating_attribute.parent.parent.parent is None or not isinstance(window_heating_attribute.parent.parent.parent, SkodaVehicle):
|
|
1384
|
+
raise SetterError('Object hierarchy is not as expected')
|
|
1385
|
+
vehicle: SkodaVehicle = window_heating_attribute.parent.parent.parent
|
|
1386
|
+
vin: Optional[str] = vehicle.vin.value
|
|
1387
|
+
if vin is None:
|
|
1388
|
+
raise SetterError('VIN in object hierarchy missing')
|
|
1389
|
+
setting_dict = {}
|
|
1390
|
+
# Round target temperature to nearest 0.5
|
|
1391
|
+
setting_dict['windowHeatingEnabled'] = window_heating_value
|
|
1392
|
+
|
|
1393
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/settings/ac-at-unlock'
|
|
1394
|
+
settings_response: requests.Response = self.session.post(url, data=json.dumps(setting_dict), allow_redirects=True)
|
|
1395
|
+
if settings_response.status_code != requests.codes['accepted']:
|
|
1396
|
+
LOG.error('Could not set air conditioning window heating (%s)', settings_response.status_code)
|
|
1397
|
+
raise SetterError(f'Could not set value ({settings_response.status_code})')
|
|
1398
|
+
return window_heating_value
|
|
1399
|
+
|
|
1400
|
+
def __on_air_conditioning_start_stop(self, start_stop_command: ClimatizationStartStopCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
|
1401
|
+
-> Union[str, Dict[str, Any]]:
|
|
1402
|
+
if start_stop_command.parent is None or start_stop_command.parent.parent is None \
|
|
1403
|
+
or start_stop_command.parent.parent.parent is None or not isinstance(start_stop_command.parent.parent.parent, SkodaVehicle):
|
|
1404
|
+
raise SetterError('Object hierarchy is not as expected')
|
|
1405
|
+
if not isinstance(command_arguments, dict):
|
|
1406
|
+
raise SetterError('Command arguments are not a dictionary')
|
|
1407
|
+
vehicle: SkodaVehicle = start_stop_command.parent.parent.parent
|
|
1408
|
+
vin: Optional[str] = vehicle.vin.value
|
|
1409
|
+
if vin is None:
|
|
1410
|
+
raise SetterError('VIN in object hierarchy missing')
|
|
1411
|
+
if 'command' not in command_arguments:
|
|
1412
|
+
raise SetterError('Command argument missing')
|
|
1413
|
+
command_dict = {}
|
|
1414
|
+
if command_arguments['command'] == ClimatizationStartStopCommand.Command.START:
|
|
1415
|
+
command_dict['heaterSource'] = 'ELECTRIC'
|
|
1416
|
+
command_dict['targetTemperature'] = {}
|
|
1417
|
+
if 'target_temperature' in command_arguments:
|
|
1418
|
+
# Round target temperature to nearest 0.5
|
|
1419
|
+
command_dict['targetTemperature']['temperatureValue'] = round(command_arguments['target_temperature'] * 2) / 2
|
|
1420
|
+
if 'target_temperature_unit' in command_arguments:
|
|
1421
|
+
if not isinstance(command_arguments['target_temperature_unit'], Temperature):
|
|
1422
|
+
raise SetterError('Temperature unit is not of type Temperature')
|
|
1423
|
+
if command_arguments['target_temperature_unit'] == Temperature.C:
|
|
1424
|
+
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1425
|
+
elif command_arguments['target_temperature_unit'] == Temperature.F:
|
|
1426
|
+
command_dict['targetTemperature']['unitInCar'] = 'FAHRENHEIT'
|
|
1427
|
+
elif command_arguments['target_temperature_unit'] == Temperature.K:
|
|
1428
|
+
command_dict['targetTemperature']['unitInCar'] = 'KELVIN'
|
|
1429
|
+
else:
|
|
1430
|
+
raise SetterError(f'Unknown temperature unit {command_arguments['target_temperature_unit']}')
|
|
1431
|
+
else:
|
|
1432
|
+
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1433
|
+
elif start_stop_command.parent is not None and (climatization := start_stop_command.parent.parent) is not None \
|
|
1434
|
+
and isinstance(climatization, Climatization) and climatization.settings is not None \
|
|
1435
|
+
and climatization.settings.target_temperature is not None and climatization.settings.target_temperature.enabled \
|
|
1436
|
+
and climatization.settings.target_temperature.value is not None: # pylint: disable=too-many-boolean-expressions
|
|
1437
|
+
# Round target temperature to nearest 0.5
|
|
1438
|
+
command_dict['targetTemperature']['temperatureValue'] = round(climatization.settings.target_temperature.value * 2) / 2
|
|
1439
|
+
if climatization.settings.target_temperature.unit == Temperature.C:
|
|
1440
|
+
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1441
|
+
elif climatization.settings.target_temperature.unit == Temperature.F:
|
|
1442
|
+
command_dict['targetTemperature']['unitInCar'] = 'FAHRENHEIT'
|
|
1443
|
+
elif climatization.settings.target_temperature.unit == Temperature.K:
|
|
1444
|
+
command_dict['targetTemperature']['unitInCar'] = 'KELVIN'
|
|
1445
|
+
else:
|
|
1446
|
+
raise SetterError(f'Unknown temperature unit {climatization.settings.target_temperature.unit}')
|
|
1447
|
+
else:
|
|
1448
|
+
command_dict['targetTemperature']['temperatureValue'] = 25.0
|
|
1449
|
+
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1450
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/start'
|
|
1451
|
+
settings_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
|
1452
|
+
elif command_arguments['command'] == ClimatizationStartStopCommand.Command.STOP:
|
|
1453
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/stop'
|
|
1454
|
+
settings_response: requests.Response = self.session.post(url, allow_redirects=True)
|
|
1455
|
+
else:
|
|
1456
|
+
raise SetterError(f'Unknown command {command_arguments["command"]}')
|
|
1457
|
+
|
|
1458
|
+
if settings_response.status_code != requests.codes['accepted']:
|
|
1459
|
+
LOG.error('Could not start/stop air conditioning (%s: %s)', settings_response.status_code, settings_response.text)
|
|
1460
|
+
raise SetterError(f'Could not start/stop air conditioning ({settings_response.status_code}: {settings_response.text})')
|
|
1461
|
+
return command_arguments
|
|
1462
|
+
|
|
1463
|
+
def __on_charging_start_stop(self, start_stop_command: ChargingStartStopCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
|
1464
|
+
-> Union[str, Dict[str, Any]]:
|
|
1465
|
+
if start_stop_command.parent is None or start_stop_command.parent.parent is None \
|
|
1466
|
+
or start_stop_command.parent.parent.parent is None or not isinstance(start_stop_command.parent.parent.parent, SkodaVehicle):
|
|
1467
|
+
raise SetterError('Object hierarchy is not as expected')
|
|
1468
|
+
if not isinstance(command_arguments, dict):
|
|
1469
|
+
raise SetterError('Command arguments are not a dictionary')
|
|
1470
|
+
vehicle: SkodaVehicle = start_stop_command.parent.parent.parent
|
|
1471
|
+
vin: Optional[str] = vehicle.vin.value
|
|
1472
|
+
if vin is None:
|
|
1473
|
+
raise SetterError('VIN in object hierarchy missing')
|
|
1474
|
+
if 'command' not in command_arguments:
|
|
1475
|
+
raise SetterError('Command argument missing')
|
|
1476
|
+
if command_arguments['command'] == ChargingStartStopCommand.Command.START:
|
|
1477
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}/start'
|
|
1478
|
+
settings_response: requests.Response = self.session.post(url, allow_redirects=True)
|
|
1479
|
+
elif command_arguments['command'] == ChargingStartStopCommand.Command.STOP:
|
|
1480
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}/stop'
|
|
1481
|
+
settings_response: requests.Response = self.session.post(url, allow_redirects=True)
|
|
1482
|
+
else:
|
|
1483
|
+
raise SetterError(f'Unknown command {command_arguments["command"]}')
|
|
1484
|
+
|
|
1485
|
+
if settings_response.status_code != requests.codes['accepted']:
|
|
1486
|
+
LOG.error('Could not start/stop charging (%s: %s)', settings_response.status_code, settings_response.text)
|
|
1487
|
+
raise SetterError(f'Could not start/stop charging ({settings_response.status_code}: {settings_response.text})')
|
|
1488
|
+
return command_arguments
|
|
@@ -21,7 +21,7 @@ from carconnectivity.drive import ElectricDrive
|
|
|
21
21
|
from carconnectivity.util import robust_time_parse, log_extra_keys
|
|
22
22
|
from carconnectivity.charging import Charging
|
|
23
23
|
from carconnectivity.climatization import Climatization
|
|
24
|
-
from carconnectivity.units import Speed, Power
|
|
24
|
+
from carconnectivity.units import Speed, Power, Length
|
|
25
25
|
|
|
26
26
|
from carconnectivity_connectors.skoda.vehicle import SkodaVehicle, SkodaElectricVehicle
|
|
27
27
|
from carconnectivity_connectors.skoda.charging import SkodaCharging, mapping_skoda_charging_state
|
|
@@ -497,7 +497,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
497
497
|
electric_drive.level._set_value(measured=measured_at, value=data['data']['soc']) # pylint: disable=protected-access
|
|
498
498
|
if 'chargedRange' in data['data'] and data['data']['chargedRange'] is not None:
|
|
499
499
|
# pylint: disable-next=protected-access
|
|
500
|
-
electric_drive.range._set_value(measured=measured_at, value=data['data']['chargedRange'])
|
|
500
|
+
electric_drive.range._set_value(measured=measured_at, value=data['data']['chargedRange'], unit=Length.KM)
|
|
501
501
|
# If charging state changed, fetch charging again
|
|
502
502
|
if old_charging_state != charging_state:
|
|
503
503
|
try:
|
|
@@ -514,7 +514,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
514
514
|
estimated_date_reached: Optional[datetime] = None
|
|
515
515
|
# pylint: disable-next=protected-access
|
|
516
516
|
vehicle.charging.estimated_date_reached._set_value(measured=measured_at, value=estimated_date_reached)
|
|
517
|
-
log_extra_keys(LOG_API, 'data', data['data'], {'vin', 'userId', 'soc', 'chargedRange', 'timeToFinish', 'state'})
|
|
517
|
+
log_extra_keys(LOG_API, 'data', data['data'], {'vin', 'userId', 'soc', 'chargedRange', 'timeToFinish', 'state', 'mode'})
|
|
518
518
|
LOG.debug('Received %s event for vehicle %s from user %s', data['name'], vin, user_id)
|
|
519
519
|
return
|
|
520
520
|
else:
|
|
@@ -530,7 +530,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
530
530
|
try:
|
|
531
531
|
self._skoda_connector.fetch_air_conditioning(vehicle, no_cache=True)
|
|
532
532
|
except CarConnectivityError as e:
|
|
533
|
-
LOG.error('Error while fetching
|
|
533
|
+
LOG.error('Error while fetching air conditioning: %s', e)
|
|
534
534
|
elif 'name' in data and data['name'] == 'climatisation-completed':
|
|
535
535
|
if 'data' in data and data['data'] is not None:
|
|
536
536
|
vehicle: Optional[GenericVehicle] = self._skoda_connector.car_connectivity.garage.get_vehicle(vin)
|
|
@@ -599,7 +599,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
599
599
|
return
|
|
600
600
|
LOG_API.info('Received unknown service event %s for vehicle %s from user %s: %s', service_event, vin, user_id, msg.payload)
|
|
601
601
|
return
|
|
602
|
-
#
|
|
602
|
+
# operation-requests
|
|
603
603
|
match = re.match(r'^(?P<user_id>[0-9a-fA-F-]+)/(?P<vin>[A-Z0-9]+)/operation-request/(?P<operation_request>[a-zA-Z0-9-_/]+)$', msg.topic)
|
|
604
604
|
if match:
|
|
605
605
|
user_id: str = match.group('user_id')
|
|
@@ -625,7 +625,10 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
625
625
|
except CarConnectivityError as e:
|
|
626
626
|
LOG.error('Error while fetching air-conditioning: %s', e)
|
|
627
627
|
return
|
|
628
|
-
|
|
628
|
+
elif data['status'] == 'IN_PROGRESS':
|
|
629
|
+
LOG.debug('Received %s operation request for vehicle %s from user %s', operation_request, vin, user_id)
|
|
630
|
+
return
|
|
631
|
+
elif operation_request == 'charging/start-stop-charging' \
|
|
629
632
|
or operation_request == 'charging/update-battery-support' \
|
|
630
633
|
or operation_request == 'charging/update-auto-unlock-plug' \
|
|
631
634
|
or operation_request == 'charging/update-care-mode' \
|
|
@@ -642,6 +645,9 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
642
645
|
except CarConnectivityError as e:
|
|
643
646
|
LOG.error('Error while fetching charging: %s', e)
|
|
644
647
|
return
|
|
648
|
+
elif data['status'] == 'IN_PROGRESS':
|
|
649
|
+
LOG.debug('Received %s operation request for vehicle %s from user %s', operation_request, vin, user_id)
|
|
650
|
+
return
|
|
645
651
|
LOG_API.info('Received unknown operation request %s for vehicle %s from user %s: %s', operation_request, vin, user_id, msg.payload)
|
|
646
652
|
return
|
|
647
653
|
LOG_API.info('I don\'t understand message %s: %s', msg.topic, msg.payload)
|
|
@@ -27,6 +27,7 @@ class SkodaVehicle(GenericVehicle): # pylint: disable=too-many-instance-attribu
|
|
|
27
27
|
else:
|
|
28
28
|
super().__init__(vin=vin, garage=garage, managing_connector=managing_connector)
|
|
29
29
|
self.capabilities = Capabilities(vehicle=self)
|
|
30
|
+
self.manufacturer._set_value(value='Škoda') # pylint: disable=protected-access
|
|
30
31
|
|
|
31
32
|
def __str__(self) -> str:
|
|
32
33
|
return_string: str = f'\t{self.capabilities}\n'
|
|
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.1a18 → carconnectivity_connector_skoda-0.1a20}/.gitignore
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/doc/Config.md
RENAMED
|
File without changes
|
{carconnectivity_connector_skoda-0.1a18 → carconnectivity_connector_skoda-0.1a20}/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
|
|
File without changes
|