carconnectivity-connector-skoda 0.1a17__tar.gz → 0.1a19__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.1a17 → carconnectivity_connector_skoda-0.1a19}/PKG-INFO +1 -1
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connector_skoda.egg-info/PKG-INFO +1 -1
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connector_skoda.egg-info/SOURCES.txt +1 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/_version.py +1 -1
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/charging.py +3 -3
- carconnectivity_connector_skoda-0.1a19/src/carconnectivity_connectors/skoda/climatization.py +41 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/connector.py +338 -16
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/error.py +11 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/mqtt_client.py +33 -4
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/.flake8 +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/.github/dependabot.yml +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/.github/workflows/build.yml +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/.github/workflows/build_and_publish.yml +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/.github/workflows/codeql-analysis.yml +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/.gitignore +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/LICENSE +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/Makefile +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/README.md +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/doc/Config.md +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/pyproject.toml +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/setup.cfg +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/setup_requirements.txt +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connector_skoda.egg-info/dependency_links.txt +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connector_skoda.egg-info/requires.txt +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connector_skoda.egg-info/top_level.txt +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/__init__.py +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/auth/__init__.py +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/auth/auth_util.py +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/auth/helpers/blacklist_retry.py +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/auth/my_skoda_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/auth/openid_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/auth/session_manager.py +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/auth/skoda_web_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/capability.py +0 -0
- {carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/src/carconnectivity_connectors/skoda/vehicle.py +0 -0
|
@@ -21,6 +21,7 @@ src/carconnectivity_connectors/skoda/__init__.py
|
|
|
21
21
|
src/carconnectivity_connectors/skoda/_version.py
|
|
22
22
|
src/carconnectivity_connectors/skoda/capability.py
|
|
23
23
|
src/carconnectivity_connectors/skoda/charging.py
|
|
24
|
+
src/carconnectivity_connectors/skoda/climatization.py
|
|
24
25
|
src/carconnectivity_connectors/skoda/connector.py
|
|
25
26
|
src/carconnectivity_connectors/skoda/error.py
|
|
26
27
|
src/carconnectivity_connectors/skoda/mqtt_client.py
|
|
@@ -97,9 +97,9 @@ class SkodaCharging(Charging): # pylint: disable=too-many-instance-attributes
|
|
|
97
97
|
ONLY_OWN_CURRENT = 'ONLY_OWN_CURRENT'
|
|
98
98
|
PREFERRED_CHARGING_TIMES = 'PREFERRED_CHARGING_TIMES'
|
|
99
99
|
TIMER_CHARGING_WITH_CLIMATISATION = 'TIMER_CHARGING_WITH_CLIMATISATION'
|
|
100
|
-
TIMER = '
|
|
101
|
-
MANUAL = '
|
|
102
|
-
OFF = '
|
|
100
|
+
TIMER = 'timer'
|
|
101
|
+
MANUAL = 'manual'
|
|
102
|
+
OFF = 'off'
|
|
103
103
|
UNKNOWN = 'unknown charge mode'
|
|
104
104
|
|
|
105
105
|
class SkodaChargingCareMode(Enum,):
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for charging for skoda vehicles.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from carconnectivity.climatization import Climatization
|
|
8
|
+
from carconnectivity.objects import GenericObject
|
|
9
|
+
from carconnectivity.vehicle import ElectricVehicle
|
|
10
|
+
|
|
11
|
+
from carconnectivity_connectors.skoda.error import Error
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from typing import Optional, Dict
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SkodaClimatization(Climatization): # pylint: disable=too-many-instance-attributes
|
|
18
|
+
"""
|
|
19
|
+
SkodaClimatization class for handling Skoda vehicle climatization information.
|
|
20
|
+
|
|
21
|
+
This class extends the Climatization class and includes an enumeration of various
|
|
22
|
+
charging states specific to Skoda vehicles.
|
|
23
|
+
"""
|
|
24
|
+
def __init__(self, vehicle: ElectricVehicle | None = None, origin: Optional[Climatization] = None) -> None:
|
|
25
|
+
if origin is not None:
|
|
26
|
+
super().__init__(origin=origin)
|
|
27
|
+
self.settings: Climatization.Settings = SkodaClimatization.Settings(origin=origin.settings)
|
|
28
|
+
else:
|
|
29
|
+
super().__init__(vehicle=vehicle)
|
|
30
|
+
self.settings: Climatization.Settings = SkodaClimatization.Settings(origin=self.settings)
|
|
31
|
+
self.errors: Dict[str, Error] = {}
|
|
32
|
+
|
|
33
|
+
class Settings(Climatization.Settings):
|
|
34
|
+
"""
|
|
35
|
+
This class represents the settings for a skoda car climatiation.
|
|
36
|
+
"""
|
|
37
|
+
def __init__(self, parent: Optional[GenericObject] = None, origin: Optional[Climatization.Settings] = None) -> None:
|
|
38
|
+
if origin is not None:
|
|
39
|
+
super().__init__(origin=origin)
|
|
40
|
+
else:
|
|
41
|
+
super().__init__(parent=parent)
|
|
@@ -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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
from carconnectivity.doors import Doors
|
|
22
|
+
from carconnectivity.windows import Windows
|
|
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
|
|
27
31
|
|
|
28
32
|
from carconnectivity_connectors.base.connector import BaseConnector
|
|
29
33
|
from carconnectivity_connectors.skoda.auth.session_manager import SessionManager, SessionUser, Service
|
|
@@ -31,12 +35,13 @@ from carconnectivity_connectors.skoda.auth.my_skoda_session import MySkodaSessio
|
|
|
31
35
|
from carconnectivity_connectors.skoda.vehicle import SkodaVehicle, SkodaElectricVehicle, SkodaCombustionVehicle, SkodaHybridVehicle
|
|
32
36
|
from carconnectivity_connectors.skoda.capability import Capability
|
|
33
37
|
from carconnectivity_connectors.skoda.charging import SkodaCharging, mapping_skoda_charging_state
|
|
38
|
+
from carconnectivity_connectors.skoda.climatization import SkodaClimatization
|
|
34
39
|
from carconnectivity_connectors.skoda.error import Error
|
|
35
40
|
from carconnectivity_connectors.skoda._version import __version__
|
|
36
41
|
from carconnectivity_connectors.skoda.mqtt_client import SkodaMQTTClient
|
|
37
42
|
|
|
38
43
|
if TYPE_CHECKING:
|
|
39
|
-
from typing import Dict, List, Optional, Any, Set
|
|
44
|
+
from typing import Dict, List, Optional, Any, Set, Union
|
|
40
45
|
|
|
41
46
|
from carconnectivity.carconnectivity import CarConnectivity
|
|
42
47
|
|
|
@@ -170,10 +175,10 @@ class Connector(BaseConnector):
|
|
|
170
175
|
self.update_vehicles()
|
|
171
176
|
self.last_update._set_value(value=datetime.now(tz=timezone.utc)) # pylint: disable=protected-access
|
|
172
177
|
if self.interval.value is not None:
|
|
173
|
-
interval:
|
|
178
|
+
interval: float = self.interval.value.total_seconds()
|
|
174
179
|
except Exception:
|
|
175
180
|
if self.interval.value is not None:
|
|
176
|
-
interval:
|
|
181
|
+
interval: float = self.interval.value.total_seconds()
|
|
177
182
|
raise
|
|
178
183
|
except TooManyRequestsError as err:
|
|
179
184
|
LOG.error('Retrieval error during update. Too many requests from your account (%s). Will try again after 15 minutes', str(err))
|
|
@@ -309,6 +314,7 @@ class Connector(BaseConnector):
|
|
|
309
314
|
for vin in set(garage.list_vehicle_vins()):
|
|
310
315
|
vehicle_to_update: Optional[GenericVehicle] = garage.get_vehicle(vin)
|
|
311
316
|
if vehicle_to_update is not None and isinstance(vehicle_to_update, SkodaVehicle) and vehicle_to_update.is_managed_by_connector(self):
|
|
317
|
+
vehicle_to_update = self.fetch_vehicle_status(vehicle_to_update)
|
|
312
318
|
vehicle_to_update = self.fetch_driving_range(vehicle_to_update)
|
|
313
319
|
if vehicle_to_update.capabilities is not None and vehicle_to_update.capabilities.enabled:
|
|
314
320
|
if vehicle_to_update.capabilities.has_capability('PARKING_POSITION'):
|
|
@@ -602,6 +608,13 @@ class Connector(BaseConnector):
|
|
|
602
608
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}'
|
|
603
609
|
data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
|
604
610
|
if data is not None:
|
|
611
|
+
if vehicle.climatization is not None and vehicle.climatization.commands is not None \
|
|
612
|
+
and not vehicle.climatization.commands.contains_command('start-stop'):
|
|
613
|
+
start_stop_command = ClimatizationStartStopCommand(parent=vehicle.climatization.commands)
|
|
614
|
+
start_stop_command._add_on_set_hook(self.__on_air_conditioning_start_stop) # pylint: disable=protected-access
|
|
615
|
+
start_stop_command.enabled = True
|
|
616
|
+
vehicle.climatization.commands.add_command(start_stop_command)
|
|
617
|
+
|
|
605
618
|
if 'carCapturedTimestamp' in data and data['carCapturedTimestamp'] is not None:
|
|
606
619
|
captured_at: datetime = robust_time_parse(data['carCapturedTimestamp'])
|
|
607
620
|
else:
|
|
@@ -624,6 +637,9 @@ class Connector(BaseConnector):
|
|
|
624
637
|
else:
|
|
625
638
|
vehicle.climatization.estimated_date_reached._set_value(value=None, measured=captured_at) # pylint: disable=protected-access
|
|
626
639
|
if 'targetTemperature' in data and data['targetTemperature'] is not None:
|
|
640
|
+
# pylint: disable-next=protected-access
|
|
641
|
+
vehicle.climatization.settings.target_temperature._add_on_set_hook(self.__on_air_conditioning_target_temperature_change)
|
|
642
|
+
vehicle.climatization.settings.target_temperature._is_changeable = True # pylint: disable=protected-access
|
|
627
643
|
unit: Temperature = Temperature.UNKNOWN
|
|
628
644
|
if 'unitInCar' in data['targetTemperature'] and data['targetTemperature']['unitInCar'] is not None:
|
|
629
645
|
if data['targetTemperature']['unitInCar'] == 'CELSIUS':
|
|
@@ -636,15 +652,16 @@ class Connector(BaseConnector):
|
|
|
636
652
|
LOG_API.info('Unknown temperature unit for targetTemperature in air-conditioning %s', data['targetTemperature']['unitInCar'])
|
|
637
653
|
if 'temperatureValue' in data['targetTemperature'] and data['targetTemperature']['temperatureValue'] is not None:
|
|
638
654
|
# pylint: disable-next=protected-access
|
|
639
|
-
vehicle.climatization.target_temperature._set_value(value=data['targetTemperature']['temperatureValue'],
|
|
640
|
-
|
|
641
|
-
|
|
655
|
+
vehicle.climatization.settings.target_temperature._set_value(value=data['targetTemperature']['temperatureValue'],
|
|
656
|
+
measured=captured_at,
|
|
657
|
+
unit=unit)
|
|
642
658
|
else:
|
|
643
|
-
|
|
659
|
+
# pylint: disable-next=protected-access
|
|
660
|
+
vehicle.climatization.settings.target_temperature._set_value(value=None, measured=captured_at, unit=unit)
|
|
644
661
|
log_extra_keys(LOG_API, 'targetTemperature', data['targetTemperature'], {'unitInCar', 'temperatureValue'})
|
|
645
662
|
else:
|
|
646
663
|
# pylint: disable-next=protected-access
|
|
647
|
-
vehicle.climatization.target_temperature._set_value(value=None, measured=captured_at, unit=Temperature.UNKNOWN)
|
|
664
|
+
vehicle.climatization.settings.target_temperature._set_value(value=None, measured=captured_at, unit=Temperature.UNKNOWN)
|
|
648
665
|
if 'outsideTemperature' in data and data['outsideTemperature'] is not None:
|
|
649
666
|
if 'carCapturedTimestamp' in data['outsideTemperature'] and data['outsideTemperature']['carCapturedTimestamp'] is not None:
|
|
650
667
|
outside_captured_at: datetime = robust_time_parse(data['outsideTemperature']['carCapturedTimestamp'])
|
|
@@ -659,7 +676,7 @@ class Connector(BaseConnector):
|
|
|
659
676
|
elif data['outsideTemperature']['temperatureUnit'] == 'KELVIN':
|
|
660
677
|
unit = Temperature.K
|
|
661
678
|
else:
|
|
662
|
-
LOG_API.info('Unknown temperature unit for outsideTemperature in air-conditioning %s', data['
|
|
679
|
+
LOG_API.info('Unknown temperature unit for outsideTemperature in air-conditioning %s', data['outsideTemperature']['temperatureUnit'])
|
|
663
680
|
if 'temperatureValue' in data['outsideTemperature'] and data['outsideTemperature']['temperatureValue'] is not None:
|
|
664
681
|
# pylint: disable-next=protected-access
|
|
665
682
|
vehicle.outside_temperature._set_value(value=data['outsideTemperature']['temperatureValue'],
|
|
@@ -674,6 +691,72 @@ class Connector(BaseConnector):
|
|
|
674
691
|
log_extra_keys(LOG_API, 'targetTemperature', data['outsideTemperature'], {'carCapturedTimestamp', 'temperatureUnit', 'temperatureValue'})
|
|
675
692
|
else:
|
|
676
693
|
vehicle.outside_temperature._set_value(value=None, measured=None, unit=Temperature.UNKNOWN) # pylint: disable=protected-access
|
|
694
|
+
if 'airConditioningAtUnlock' in data and data['airConditioningAtUnlock'] is not None:
|
|
695
|
+
if vehicle.climatization is not None and vehicle.climatization.settings is not None:
|
|
696
|
+
# pylint: disable-next=protected-access
|
|
697
|
+
vehicle.climatization.settings.climatization_at_unlock._add_on_set_hook(self.__on_air_conditioning_at_unlock_change)
|
|
698
|
+
vehicle.climatization.settings.climatization_at_unlock._is_changeable = True # pylint: disable=protected-access
|
|
699
|
+
if data['airConditioningAtUnlock'] is True:
|
|
700
|
+
# pylint: disable-next=protected-access
|
|
701
|
+
vehicle.climatization.settings.climatization_at_unlock._set_value(True, measured=captured_at)
|
|
702
|
+
elif data['airConditioningAtUnlock'] is False:
|
|
703
|
+
# pylint: disable-next=protected-access
|
|
704
|
+
vehicle.climatization.settings.climatization_at_unlock._set_value(False, measured=captured_at)
|
|
705
|
+
else:
|
|
706
|
+
# pylint: disable-next=protected-access
|
|
707
|
+
vehicle.climatization.settings.climatization_at_unlock._set_value(None, measured=captured_at)
|
|
708
|
+
else:
|
|
709
|
+
if vehicle.climatization is not None and vehicle.climatization.settings is not None:
|
|
710
|
+
# pylint: disable-next=protected-access
|
|
711
|
+
vehicle.climatization.settings.climatization_at_unlock._set_value(None, measured=captured_at)
|
|
712
|
+
if 'steeringWheelPosition' in data and data['steeringWheelPosition'] is not None:
|
|
713
|
+
if vehicle.specification is not None:
|
|
714
|
+
if data['steeringWheelPosition'] in [item.name for item in GenericVehicle.VehicleSpecification.SteeringPosition]:
|
|
715
|
+
steering_wheel_position: GenericVehicle.VehicleSpecification.SteeringPosition = \
|
|
716
|
+
GenericVehicle.VehicleSpecification.SteeringPosition[data['steeringWheelPosition']]
|
|
717
|
+
else:
|
|
718
|
+
LOG_API.info('Unknown steering wheel position %s not in %s', data['steeringWheelPosition'],
|
|
719
|
+
str(GenericVehicle.VehicleSpecification.SteeringPosition))
|
|
720
|
+
steering_wheel_position = GenericVehicle.VehicleSpecification.SteeringPosition.UNKNOWN
|
|
721
|
+
# pylint: disable-next=protected-access
|
|
722
|
+
vehicle.specification.steering_wheel_position._set_value(value=steering_wheel_position, measured=captured_at)
|
|
723
|
+
else:
|
|
724
|
+
if vehicle.specification is not None:
|
|
725
|
+
# pylint: disable-next=protected-access
|
|
726
|
+
vehicle.specification.steering_wheel_position._set_value(None, measured=captured_at)
|
|
727
|
+
if 'windowHeatingEnabled' in data and data['windowHeatingEnabled'] is not None:
|
|
728
|
+
if vehicle.climatization is not None and vehicle.climatization.settings is not None:
|
|
729
|
+
# pylint: disable-next=protected-access
|
|
730
|
+
vehicle.climatization.settings.window_heating._add_on_set_hook(self.__on_air_conditioning_window_heating_change)
|
|
731
|
+
vehicle.climatization.settings.window_heating._is_changeable = True # pylint: disable=protected-access
|
|
732
|
+
if data['windowHeatingEnabled'] is True:
|
|
733
|
+
# pylint: disable-next=protected-access
|
|
734
|
+
vehicle.climatization.settings.window_heating._set_value(True, measured=captured_at)
|
|
735
|
+
elif data['windowHeatingEnabled'] is False:
|
|
736
|
+
# pylint: disable-next=protected-access
|
|
737
|
+
vehicle.climatization.settings.window_heating._set_value(False, measured=captured_at)
|
|
738
|
+
else:
|
|
739
|
+
# pylint: disable-next=protected-access
|
|
740
|
+
vehicle.climatization.settings.window_heating._set_value(None, measured=captured_at)
|
|
741
|
+
else:
|
|
742
|
+
if vehicle.climatization is not None and vehicle.climatization.settings is not None:
|
|
743
|
+
# pylint: disable-next=protected-access
|
|
744
|
+
vehicle.climatization.settings.window_heating._set_value(None, measured=captured_at)
|
|
745
|
+
if 'seatHeatingActivated' in data and data['seatHeatingActivated'] is not None:
|
|
746
|
+
if vehicle.climatization is not None and vehicle.climatization.settings is not None:
|
|
747
|
+
if data['seatHeatingActivated'] is True:
|
|
748
|
+
# pylint: disable-next=protected-access
|
|
749
|
+
vehicle.climatization.settings.seat_heating._set_value(True, measured=captured_at)
|
|
750
|
+
elif data['seatHeatingActivated'] is False:
|
|
751
|
+
# pylint: disable-next=protected-access
|
|
752
|
+
vehicle.climatization.settings.seat_heating._set_value(False, measured=captured_at)
|
|
753
|
+
else:
|
|
754
|
+
# pylint: disable-next=protected-access
|
|
755
|
+
vehicle.climatization.settings.seat_heating._set_value(None, measured=captured_at)
|
|
756
|
+
else:
|
|
757
|
+
if vehicle.climatization is not None and vehicle.climatization.settings is not None:
|
|
758
|
+
# pylint: disable-next=protected-access
|
|
759
|
+
vehicle.climatization.settings.seat_heating._set_value(None, measured=captured_at)
|
|
677
760
|
if isinstance(vehicle, SkodaElectricVehicle):
|
|
678
761
|
if 'chargerConnectionState' in data and data['chargerConnectionState'] is not None \
|
|
679
762
|
and vehicle.charging is not None and vehicle.charging.connector is not None:
|
|
@@ -707,9 +790,35 @@ class Connector(BaseConnector):
|
|
|
707
790
|
vehicle.charging.connector.lock_state._set_value(value=None, measured=captured_at)
|
|
708
791
|
if 'windowHeatingState' in data and data['windowHeatingState'] is not None:
|
|
709
792
|
pass
|
|
710
|
-
|
|
793
|
+
if 'errors' in data and data['errors'] is not None:
|
|
794
|
+
found_errors: Set[str] = set()
|
|
795
|
+
if not isinstance(vehicle.climatization, SkodaClimatization):
|
|
796
|
+
vehicle.climatization = SkodaClimatization(origin=vehicle.climatization)
|
|
797
|
+
for error_dict in data['errors']:
|
|
798
|
+
if 'type' in error_dict and error_dict['type'] is not None:
|
|
799
|
+
if error_dict['type'] not in vehicle.climatization.errors:
|
|
800
|
+
error: Error = Error(object_id=error_dict['type'])
|
|
801
|
+
else:
|
|
802
|
+
error = vehicle.climatization.errors[error_dict['type']]
|
|
803
|
+
if error_dict['type'] in [item.name for item in Error.ClimatizationError]:
|
|
804
|
+
error_type: Error.ClimatizationError = Error.ClimatizationError[error_dict['type']]
|
|
805
|
+
else:
|
|
806
|
+
LOG_API.info('Unknown climatization error type %s not in %s', error_dict['type'], str(Error.ClimatizationError))
|
|
807
|
+
error_type = Error.ClimatizationError.UNKNOWN
|
|
808
|
+
error.type._set_value(error_type, measured=captured_at) # pylint: disable=protected-access
|
|
809
|
+
if 'description' in error_dict and error_dict['description'] is not None:
|
|
810
|
+
error.description._set_value(error_dict['description'], measured=captured_at) # pylint: disable=protected-access
|
|
811
|
+
log_extra_keys(LOG_API, 'errors', error_dict, {'type', 'description'})
|
|
812
|
+
if vehicle.climatization is not None and vehicle.climatization.errors is not None and len(vehicle.climatization.errors) > 0:
|
|
813
|
+
for error_id in vehicle.climatization.errors.keys()-found_errors:
|
|
814
|
+
vehicle.climatization.errors.pop(error_id)
|
|
815
|
+
else:
|
|
816
|
+
if isinstance(vehicle.climatization, SkodaClimatization):
|
|
817
|
+
vehicle.climatization.errors.clear()
|
|
818
|
+
log_extra_keys(LOG_API, 'air-condition', data, {'carCapturedTimestamp', 'state', 'estimatedDateTimeToReachTargetTemperature',
|
|
711
819
|
'targetTemperature', 'outsideTemperature', 'chargerConnectionState',
|
|
712
|
-
'chargerLockState'
|
|
820
|
+
'chargerLockState', 'airConditioningAtUnlock', 'steeringWheelPosition',
|
|
821
|
+
'windowHeatingEnabled', 'seatHeatingActivated', 'errors'})
|
|
713
822
|
return vehicle
|
|
714
823
|
|
|
715
824
|
def fetch_vehicle_details(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
|
|
@@ -875,6 +984,97 @@ class Connector(BaseConnector):
|
|
|
875
984
|
'secondaryEngineRange'})
|
|
876
985
|
return vehicle
|
|
877
986
|
|
|
987
|
+
def fetch_vehicle_status(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
|
|
988
|
+
"""
|
|
989
|
+
Fetches the status of a vehicle from other Skoda API.
|
|
990
|
+
|
|
991
|
+
Args:
|
|
992
|
+
vehicle (GenericVehicle): The vehicle object containing the VIN.
|
|
993
|
+
|
|
994
|
+
Returns:
|
|
995
|
+
None
|
|
996
|
+
"""
|
|
997
|
+
vin = vehicle.vin.value
|
|
998
|
+
if vin is None:
|
|
999
|
+
raise APIError('VIN is missing')
|
|
1000
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/vehicle-status/{vin}'
|
|
1001
|
+
vehicle_status_data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
|
1002
|
+
if vehicle_status_data:
|
|
1003
|
+
captured_at: datetime = robust_time_parse(vehicle_status_data['carCapturedTimestamp'])
|
|
1004
|
+
if 'overall' in vehicle_status_data and vehicle_status_data['overall'] is not None:
|
|
1005
|
+
if 'doorsLocked' in vehicle_status_data['overall'] and vehicle_status_data['overall']['doorsLocked'] is not None \
|
|
1006
|
+
and vehicle.doors is not None:
|
|
1007
|
+
if vehicle_status_data['overall']['doorsLocked'] == 'YES':
|
|
1008
|
+
vehicle.doors.lock_state._set_value(Doors.LockState.LOCKED, measured=captured_at) # pylint: disable=protected-access
|
|
1009
|
+
vehicle.doors.open_state._set_value(Doors.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
|
|
1010
|
+
elif vehicle_status_data['overall']['doorsLocked'] == 'OPENED':
|
|
1011
|
+
vehicle.doors.lock_state._set_value(Doors.LockState.UNLOCKED, measured=captured_at) # pylint: disable=protected-access
|
|
1012
|
+
vehicle.doors.open_state._set_value(Doors.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
|
|
1013
|
+
elif vehicle_status_data['overall']['doorsLocked'] == 'UNLOCKED':
|
|
1014
|
+
vehicle.doors.lock_state._set_value(Doors.LockState.UNLOCKED, measured=captured_at) # pylint: disable=protected-access
|
|
1015
|
+
vehicle.doors.open_state._set_value(Doors.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
|
|
1016
|
+
elif vehicle_status_data['overall']['doorsLocked'] == 'TRUNK_OPENED':
|
|
1017
|
+
vehicle.doors.lock_state._set_value(Doors.LockState.UNLOCKED, measured=captured_at) # pylint: disable=protected-access
|
|
1018
|
+
vehicle.doors.open_state._set_value(Doors.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
|
|
1019
|
+
elif vehicle_status_data['overall']['doorsLocked'] == 'UNKNOWN':
|
|
1020
|
+
vehicle.doors.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1021
|
+
vehicle.doors.open_state._set_value(Doors.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1022
|
+
else:
|
|
1023
|
+
LOG_API.info('Unknown doorsLocked state %s', vehicle_status_data['overall']['doorsLocked'])
|
|
1024
|
+
vehicle.doors.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1025
|
+
vehicle.doors.open_state._set_value(Doors.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1026
|
+
if 'locked' in vehicle_status_data['overall'] and vehicle_status_data['overall']['locked'] is not None:
|
|
1027
|
+
if vehicle_status_data['overall']['locked'] == 'YES':
|
|
1028
|
+
vehicle.doors.lock_state._set_value(Doors.LockState.LOCKED, measured=captured_at) # pylint: disable=protected-access
|
|
1029
|
+
elif vehicle_status_data['overall']['locked'] == 'NO':
|
|
1030
|
+
vehicle.doors.lock_state._set_value(Doors.LockState.UNLOCKED, measured=captured_at) # pylint: disable=protected-access
|
|
1031
|
+
elif vehicle_status_data['overall']['locked'] == 'UNKNOWN':
|
|
1032
|
+
vehicle.doors.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1033
|
+
else:
|
|
1034
|
+
LOG_API.info('Unknown locked state %s', vehicle_status_data['overall']['locked'])
|
|
1035
|
+
vehicle.doors.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1036
|
+
if 'doors' in vehicle_status_data['overall'] and vehicle_status_data['overall']['doors'] is not None:
|
|
1037
|
+
if vehicle_status_data['overall']['doors'] == 'CLOSED':
|
|
1038
|
+
vehicle.doors.open_state._set_value(Doors.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
|
|
1039
|
+
elif vehicle_status_data['overall']['doors'] == 'OPEN':
|
|
1040
|
+
vehicle.doors.open_state._set_value(Doors.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
|
|
1041
|
+
elif vehicle_status_data['overall']['doors'] == 'UNSUPPORTED':
|
|
1042
|
+
vehicle.doors.open_state._set_value(Doors.OpenState.UNSUPPORTED, measured=captured_at) # pylint: disable=protected-access
|
|
1043
|
+
elif vehicle_status_data['overall']['doors'] == 'UNKNOWN':
|
|
1044
|
+
vehicle.doors.open_state._set_value(Doors.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1045
|
+
else:
|
|
1046
|
+
LOG_API.info('Unknown doors state %s', vehicle_status_data['overall']['doors'])
|
|
1047
|
+
vehicle.doors.open_state._set_value(Doors.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1048
|
+
if 'windows' in vehicle_status_data['overall'] and vehicle_status_data['overall']['windows'] is not None:
|
|
1049
|
+
if vehicle_status_data['overall']['windows'] == 'CLOSED':
|
|
1050
|
+
vehicle.windows.open_state._set_value(Windows.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
|
|
1051
|
+
elif vehicle_status_data['overall']['windows'] == 'OPEN':
|
|
1052
|
+
vehicle.windows.open_state._set_value(Windows.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
|
|
1053
|
+
elif vehicle_status_data['overall']['windows'] == 'UNKNOWN':
|
|
1054
|
+
vehicle.windows.open_state._set_value(Windows.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1055
|
+
elif vehicle_status_data['overall']['windows'] == 'UNSUPPORTED':
|
|
1056
|
+
vehicle.windows.open_state._set_value(Windows.OpenState.UNSUPPORTED, measured=captured_at) # pylint: disable=protected-access
|
|
1057
|
+
else:
|
|
1058
|
+
LOG_API.info('Unknown windows state %s', vehicle_status_data['overall']['windows'])
|
|
1059
|
+
vehicle.windows.open_state._set_value(Windows.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1060
|
+
if 'lights' in vehicle_status_data['overall'] and vehicle_status_data['overall']['lights'] is not None:
|
|
1061
|
+
if vehicle_status_data['overall']['lights'] == 'ON':
|
|
1062
|
+
vehicle.lights.light_state._set_value(Lights.LightState.ON, measured=captured_at) # pylint: disable=protected-access
|
|
1063
|
+
elif vehicle_status_data['overall']['lights'] == 'OFF':
|
|
1064
|
+
vehicle.lights.light_state._set_value(Lights.LightState.OFF, measured=captured_at) # pylint: disable=protected-access
|
|
1065
|
+
elif vehicle_status_data['overall']['lights'] == 'UNKNOWN':
|
|
1066
|
+
vehicle.lights.light_state._set_value(Lights.LightState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1067
|
+
else:
|
|
1068
|
+
LOG_API.info('Unknown lights state %s', vehicle_status_data['overall']['lights'])
|
|
1069
|
+
vehicle.lights.light_state._set_value(Lights.LightState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1070
|
+
log_extra_keys(LOG_API, 'status overall', vehicle_status_data['overall'], {'doorsLocked',
|
|
1071
|
+
'locked',
|
|
1072
|
+
'doors',
|
|
1073
|
+
'windows',
|
|
1074
|
+
'lights'})
|
|
1075
|
+
log_extra_keys(LOG_API, f'/api/v2/vehicle-status/{vin}', vehicle_status_data, {'overall', 'carCapturedTimestamp'})
|
|
1076
|
+
return vehicle
|
|
1077
|
+
|
|
878
1078
|
# def fetch_vehicle_status_second_api(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
|
|
879
1079
|
# """
|
|
880
1080
|
# Fetches the status of a vehicle from other Skoda API.
|
|
@@ -1115,3 +1315,125 @@ class Connector(BaseConnector):
|
|
|
1115
1315
|
|
|
1116
1316
|
def get_version(self) -> str:
|
|
1117
1317
|
return __version__
|
|
1318
|
+
|
|
1319
|
+
def __on_air_conditioning_target_temperature_change(self, temperature_attribute: TemperatureAttribute, target_temperature: float) -> float:
|
|
1320
|
+
"""
|
|
1321
|
+
Callback for the climatization target temperature change.
|
|
1322
|
+
|
|
1323
|
+
Args:
|
|
1324
|
+
temperature_attribute (TemperatureAttribute): The temperature attribute that changed.
|
|
1325
|
+
target_temperature (float): The new target temperature.
|
|
1326
|
+
"""
|
|
1327
|
+
if temperature_attribute.parent is None or temperature_attribute.parent.parent is None \
|
|
1328
|
+
or temperature_attribute.parent.parent.parent is None or not isinstance(temperature_attribute.parent.parent.parent, SkodaVehicle):
|
|
1329
|
+
raise SetterError('Object hierarchy is not as expected')
|
|
1330
|
+
vehicle: SkodaVehicle = temperature_attribute.parent.parent.parent
|
|
1331
|
+
vin: Optional[str] = vehicle.vin.value
|
|
1332
|
+
if vin is None:
|
|
1333
|
+
raise SetterError('VIN in object hierarchy missing')
|
|
1334
|
+
setting_dict = {}
|
|
1335
|
+
# Round target temperature to nearest 0.5
|
|
1336
|
+
setting_dict['temperatureValue'] = round(target_temperature * 2) / 2
|
|
1337
|
+
if temperature_attribute.unit == Temperature.C:
|
|
1338
|
+
setting_dict['unitInCar'] = 'CELSIUS'
|
|
1339
|
+
elif temperature_attribute.unit == Temperature.F:
|
|
1340
|
+
setting_dict['unitInCar'] = 'FAHRENHEIT'
|
|
1341
|
+
elif temperature_attribute.unit == Temperature.K:
|
|
1342
|
+
setting_dict['unitInCar'] = 'KELVIN'
|
|
1343
|
+
else:
|
|
1344
|
+
raise SetterError(f'Unknown temperature unit {temperature_attribute.unit}')
|
|
1345
|
+
|
|
1346
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/settings/target-temperature'
|
|
1347
|
+
settings_response: requests.Response = self.session.post(url, data=json.dumps(setting_dict), allow_redirects=True)
|
|
1348
|
+
if settings_response.status_code != requests.codes['accepted']:
|
|
1349
|
+
LOG.error('Could not set target temperature (%s)', settings_response.status_code)
|
|
1350
|
+
raise SetterError(f'Could not set value ({settings_response.status_code})')
|
|
1351
|
+
return target_temperature
|
|
1352
|
+
|
|
1353
|
+
def __on_air_conditioning_at_unlock_change(self, at_unlock_attribute: BooleanAttribute, at_unlock_value: bool) -> bool:
|
|
1354
|
+
if at_unlock_attribute.parent is None or at_unlock_attribute.parent.parent is None \
|
|
1355
|
+
or at_unlock_attribute.parent.parent.parent is None or not isinstance(at_unlock_attribute.parent.parent.parent, SkodaVehicle):
|
|
1356
|
+
raise SetterError('Object hierarchy is not as expected')
|
|
1357
|
+
vehicle: SkodaVehicle = at_unlock_attribute.parent.parent.parent
|
|
1358
|
+
vin: Optional[str] = vehicle.vin.value
|
|
1359
|
+
if vin is None:
|
|
1360
|
+
raise SetterError('VIN in object hierarchy missing')
|
|
1361
|
+
setting_dict = {}
|
|
1362
|
+
# Round target temperature to nearest 0.5
|
|
1363
|
+
setting_dict['airConditioningAtUnlockEnabled'] = at_unlock_value
|
|
1364
|
+
|
|
1365
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/settings/ac-at-unlock'
|
|
1366
|
+
settings_response: requests.Response = self.session.post(url, data=json.dumps(setting_dict), allow_redirects=True)
|
|
1367
|
+
if settings_response.status_code != requests.codes['accepted']:
|
|
1368
|
+
LOG.error('Could not set air conditioning at unlock (%s)', settings_response.status_code)
|
|
1369
|
+
raise SetterError(f'Could not set value ({settings_response.status_code})')
|
|
1370
|
+
return at_unlock_value
|
|
1371
|
+
|
|
1372
|
+
def __on_air_conditioning_window_heating_change(self, window_heating_attribute: BooleanAttribute, window_heating_value: bool) -> bool:
|
|
1373
|
+
if window_heating_attribute.parent is None or window_heating_attribute.parent.parent is None \
|
|
1374
|
+
or window_heating_attribute.parent.parent.parent is None or not isinstance(window_heating_attribute.parent.parent.parent, SkodaVehicle):
|
|
1375
|
+
raise SetterError('Object hierarchy is not as expected')
|
|
1376
|
+
vehicle: SkodaVehicle = window_heating_attribute.parent.parent.parent
|
|
1377
|
+
vin: Optional[str] = vehicle.vin.value
|
|
1378
|
+
if vin is None:
|
|
1379
|
+
raise SetterError('VIN in object hierarchy missing')
|
|
1380
|
+
setting_dict = {}
|
|
1381
|
+
# Round target temperature to nearest 0.5
|
|
1382
|
+
setting_dict['windowHeatingEnabled'] = window_heating_value
|
|
1383
|
+
|
|
1384
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/settings/ac-at-unlock'
|
|
1385
|
+
settings_response: requests.Response = self.session.post(url, data=json.dumps(setting_dict), allow_redirects=True)
|
|
1386
|
+
if settings_response.status_code != requests.codes['accepted']:
|
|
1387
|
+
LOG.error('Could not set air conditioning window heating (%s)', settings_response.status_code)
|
|
1388
|
+
raise SetterError(f'Could not set value ({settings_response.status_code})')
|
|
1389
|
+
return window_heating_value
|
|
1390
|
+
|
|
1391
|
+
def __on_air_conditioning_start_stop(self, start_stop_command: ClimatizationStartStopCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
|
1392
|
+
-> Union[str, Dict[str, Any]]:
|
|
1393
|
+
if start_stop_command.parent is None or start_stop_command.parent.parent is None \
|
|
1394
|
+
or start_stop_command.parent.parent.parent is None or not isinstance(start_stop_command.parent.parent.parent, SkodaVehicle):
|
|
1395
|
+
raise SetterError('Object hierarchy is not as expected')
|
|
1396
|
+
if not isinstance(command_arguments, dict):
|
|
1397
|
+
raise SetterError('Command arguments are not a dictionary')
|
|
1398
|
+
vehicle: SkodaVehicle = start_stop_command.parent.parent.parent
|
|
1399
|
+
vin: Optional[str] = vehicle.vin.value
|
|
1400
|
+
if vin is None:
|
|
1401
|
+
raise SetterError('VIN in object hierarchy missing')
|
|
1402
|
+
if 'command' not in command_arguments:
|
|
1403
|
+
raise SetterError('Command argument missing')
|
|
1404
|
+
command_dict = {}
|
|
1405
|
+
if command_arguments['command'] == ClimatizationStartStopCommand.Command.START:
|
|
1406
|
+
command_dict['heaterSource'] = 'ELECTRIC'
|
|
1407
|
+
command_dict['targetTemperature'] = {}
|
|
1408
|
+
if 'target_temperature' in command_arguments:
|
|
1409
|
+
# Round target temperature to nearest 0.5
|
|
1410
|
+
command_dict['targetTemperature']['temperatureValue'] = round(command_arguments['target_temperature'] * 2) / 2
|
|
1411
|
+
if 'target_temperature_unit' in command_arguments:
|
|
1412
|
+
if not isinstance(command_arguments['target_temperature_unit'], Temperature):
|
|
1413
|
+
raise SetterError('Temperature unit is not of type Temperature')
|
|
1414
|
+
if command_arguments['target_temperature_unit'] == Temperature.C:
|
|
1415
|
+
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1416
|
+
elif command_arguments['target_temperature_unit'] == Temperature.F:
|
|
1417
|
+
command_dict['targetTemperature']['unitInCar'] = 'FAHRENHEIT'
|
|
1418
|
+
elif command_arguments['target_temperature_unit'] == Temperature.K:
|
|
1419
|
+
command_dict['targetTemperature']['unitInCar'] = 'KELVIN'
|
|
1420
|
+
else:
|
|
1421
|
+
raise SetterError(f'Unknown temperature unit {command_arguments['target_temperature_unit']}')
|
|
1422
|
+
else:
|
|
1423
|
+
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1424
|
+
else:
|
|
1425
|
+
command_dict['targetTemperature']['temperatureValue'] = 25.0
|
|
1426
|
+
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1427
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/start'
|
|
1428
|
+
print(json.dumps(command_dict))
|
|
1429
|
+
settings_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
|
1430
|
+
elif command_arguments['command'] == ClimatizationStartStopCommand.Command.STOP:
|
|
1431
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/stop'
|
|
1432
|
+
settings_response: requests.Response = self.session.post(url, allow_redirects=True)
|
|
1433
|
+
else:
|
|
1434
|
+
raise SetterError(f'Unknown command {command_arguments["command"]}')
|
|
1435
|
+
|
|
1436
|
+
if settings_response.status_code != requests.codes['accepted']:
|
|
1437
|
+
LOG.error('Could not start/stop air conditioning (%s: %s)', settings_response.status_code, settings_response.text)
|
|
1438
|
+
raise SetterError(f'Could not start/stop air conditioning ({settings_response.status_code}: {settings_response.text})')
|
|
1439
|
+
return command_arguments
|
|
@@ -40,3 +40,14 @@ class Error(GenericObject):
|
|
|
40
40
|
MAX_CHARGE_CURRENT_IS_NOT_AVAILABLE = 'MAX_CHARGE_CURRENT_IS_NOT_AVAILABLE'
|
|
41
41
|
CHARGE_LIMIT_IS_NOT_AVAILABLE = 'CHARGE_LIMIT_IS_NOT_AVAILABLE'
|
|
42
42
|
UNKNOWN = 'UNKNOWN'
|
|
43
|
+
|
|
44
|
+
class ClimatizationError(Enum):
|
|
45
|
+
"""
|
|
46
|
+
ClimatizationError is an enumeration for representing various errors
|
|
47
|
+
related to the climatization system in a Skoda car.
|
|
48
|
+
|
|
49
|
+
This enum can be extended to include specific error codes and messages
|
|
50
|
+
that correspond to different climatization issues.
|
|
51
|
+
"""
|
|
52
|
+
UNAVAILABLE_CHARGING_INFORMATION = 'UNAVAILABLE_CHARGING_INFORMATION'
|
|
53
|
+
UNKNOWN = 'UNKNOWN'
|
|
@@ -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
|
|
@@ -468,6 +468,15 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
468
468
|
if electric_drive is not None:
|
|
469
469
|
charging_state: Optional[Charging.ChargingState] = vehicle.charging.state.value
|
|
470
470
|
old_charging_state: Optional[Charging.ChargingState] = charging_state
|
|
471
|
+
if 'mode' in data['data'] and data['data']['mode'] is not None \
|
|
472
|
+
and vehicle.charging is not None and isinstance(vehicle.charging.settings, SkodaCharging.Settings):
|
|
473
|
+
if data['data']['mode'] in SkodaCharging.SkodaChargeMode:
|
|
474
|
+
skoda_charging_mode = SkodaCharging.SkodaChargeMode(data['data']['mode'])
|
|
475
|
+
else:
|
|
476
|
+
LOG_API.info('Unkown charging mode %s not in %s', data['data']['mode'], str(SkodaCharging.SkodaChargeMode))
|
|
477
|
+
skoda_charging_mode = Charging.ChargingState.UNKNOWN
|
|
478
|
+
# pylint: disable-next=protected-access
|
|
479
|
+
vehicle.charging.settings.preferred_charge_mode._set_value(value=skoda_charging_mode, measured=measured_at)
|
|
471
480
|
if 'state' in data['data'] and data['data']['state'] is not None:
|
|
472
481
|
if data['data']['state'] in [item.value for item in SkodaCharging.SkodaChargingState]:
|
|
473
482
|
skoda_charging_state = SkodaCharging.SkodaChargingState(data['data']['state'])
|
|
@@ -488,7 +497,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
488
497
|
electric_drive.level._set_value(measured=measured_at, value=data['data']['soc']) # pylint: disable=protected-access
|
|
489
498
|
if 'chargedRange' in data['data'] and data['data']['chargedRange'] is not None:
|
|
490
499
|
# pylint: disable-next=protected-access
|
|
491
|
-
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)
|
|
492
501
|
# If charging state changed, fetch charging again
|
|
493
502
|
if old_charging_state != charging_state:
|
|
494
503
|
try:
|
|
@@ -505,7 +514,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
505
514
|
estimated_date_reached: Optional[datetime] = None
|
|
506
515
|
# pylint: disable-next=protected-access
|
|
507
516
|
vehicle.charging.estimated_date_reached._set_value(measured=measured_at, value=estimated_date_reached)
|
|
508
|
-
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'})
|
|
509
518
|
LOG.debug('Received %s event for vehicle %s from user %s', data['name'], vin, user_id)
|
|
510
519
|
return
|
|
511
520
|
else:
|
|
@@ -544,6 +553,10 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
544
553
|
"""
|
|
545
554
|
vin = vehicle.id
|
|
546
555
|
self.delayed_access_function_timers.pop(vin)
|
|
556
|
+
try:
|
|
557
|
+
self._skoda_connector.fetch_vehicle_status(vehicle, no_cache=True)
|
|
558
|
+
except CarConnectivityError as e:
|
|
559
|
+
LOG.error('Error while fetching vehicle status: %s', e)
|
|
547
560
|
if vehicle.capabilities is not None and vehicle.capabilities.enabled \
|
|
548
561
|
and vehicle.capabilities.has_capability('CHARGING') and isinstance(vehicle, SkodaElectricVehicle):
|
|
549
562
|
try:
|
|
@@ -568,12 +581,25 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
568
581
|
self.delayed_access_function_timers[vin] = threading.Timer(2.0, delayed_access_function, kwargs={'vehicle': vehicle})
|
|
569
582
|
self.delayed_access_function_timers[vin].start()
|
|
570
583
|
|
|
584
|
+
LOG_API.info('Received event name %s service event %s for vehicle %s from user %s: %s', data['name'],
|
|
585
|
+
service_event, vin, user_id, msg.payload)
|
|
586
|
+
return
|
|
587
|
+
elif service_event == 'vehicle-status/lights':
|
|
588
|
+
if 'name' in data and data['name'] == 'change-lights':
|
|
589
|
+
if 'data' in data and data['data'] is not None:
|
|
590
|
+
vehicle: Optional[GenericVehicle] = self._skoda_connector.car_connectivity.garage.get_vehicle(vin)
|
|
591
|
+
if isinstance(vehicle, SkodaVehicle):
|
|
592
|
+
try:
|
|
593
|
+
self._skoda_connector.fetch_vehicle_status(vehicle, no_cache=True)
|
|
594
|
+
except CarConnectivityError as e:
|
|
595
|
+
LOG.error('Error while fetching vehicle status: %s', e)
|
|
596
|
+
|
|
571
597
|
LOG_API.info('Received event name %s service event %s for vehicle %s from user %s: %s', data['name'],
|
|
572
598
|
service_event, vin, user_id, msg.payload)
|
|
573
599
|
return
|
|
574
600
|
LOG_API.info('Received unknown service event %s for vehicle %s from user %s: %s', service_event, vin, user_id, msg.payload)
|
|
575
601
|
return
|
|
576
|
-
#
|
|
602
|
+
# operation-requests
|
|
577
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)
|
|
578
604
|
if match:
|
|
579
605
|
user_id: str = match.group('user_id')
|
|
@@ -599,6 +625,9 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
599
625
|
except CarConnectivityError as e:
|
|
600
626
|
LOG.error('Error while fetching air-conditioning: %s', e)
|
|
601
627
|
return
|
|
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
|
|
602
631
|
if operation_request == 'charging/start-stop-charging' \
|
|
603
632
|
or operation_request == 'charging/update-battery-support' \
|
|
604
633
|
or operation_request == 'charging/update-auto-unlock-plug' \
|
|
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.1a17 → carconnectivity_connector_skoda-0.1a19}/.gitignore
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/doc/Config.md
RENAMED
|
File without changes
|
{carconnectivity_connector_skoda-0.1a17 → carconnectivity_connector_skoda-0.1a19}/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
|