carconnectivity-connector-skoda 0.1a21__tar.gz → 0.2a1__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.1a21 → carconnectivity_connector_skoda-0.2a1}/.github/workflows/build.yml +3 -1
- carconnectivity_connector_skoda-0.2a1/CHANGELOG.md +14 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/PKG-INFO +2 -2
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/pyproject.toml +1 -1
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/setup_requirements.txt +1 -1
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connector_skoda.egg-info/PKG-INFO +2 -2
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connector_skoda.egg-info/SOURCES.txt +2 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connector_skoda.egg-info/requires.txt +1 -1
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/_version.py +2 -2
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/capability.py +0 -10
- carconnectivity_connector_skoda-0.2a1/src/carconnectivity_connectors/skoda/command_impl.py +72 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/connector.py +198 -211
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/mqtt_client.py +2 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/vehicle.py +0 -4
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/.flake8 +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/.github/dependabot.yml +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/.github/workflows/build_and_publish.yml +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/.github/workflows/codeql-analysis.yml +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/.gitignore +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/LICENSE +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/Makefile +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/README.md +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/doc/Config.md +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/setup.cfg +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connector_skoda.egg-info/dependency_links.txt +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connector_skoda.egg-info/top_level.txt +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/__init__.py +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/auth/__init__.py +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/auth/auth_util.py +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/auth/helpers/blacklist_retry.py +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/auth/my_skoda_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/auth/openid_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/auth/session_manager.py +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/auth/skoda_web_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/charging.py +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/climatization.py +0 -0
- {carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/src/carconnectivity_connectors/skoda/error.py +0 -0
|
@@ -10,10 +10,12 @@ on:
|
|
|
10
10
|
paths:
|
|
11
11
|
- .github/workflows/build.yml
|
|
12
12
|
- '**.py'
|
|
13
|
+
- 'pyproject.toml'
|
|
13
14
|
pull_request:
|
|
14
15
|
paths:
|
|
15
16
|
- .github/workflows/build.yml
|
|
16
17
|
- '**.py'
|
|
18
|
+
- 'pyproject.toml'
|
|
17
19
|
|
|
18
20
|
jobs:
|
|
19
21
|
build-python:
|
|
@@ -38,7 +40,7 @@ jobs:
|
|
|
38
40
|
python -m build
|
|
39
41
|
- name: Install built package
|
|
40
42
|
run: |
|
|
41
|
-
pip install dist/*.whl
|
|
43
|
+
pip install --pre dist/*.whl
|
|
42
44
|
- name: Lint
|
|
43
45
|
run: |
|
|
44
46
|
make lint
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
- No unreleased changes so far
|
|
7
|
+
|
|
8
|
+
## [0.1] - 2025-01-25
|
|
9
|
+
Initial release, let's go and give this to the public to try out...
|
|
10
|
+
The API is not yet implemented completely but most functions already work
|
|
11
|
+
|
|
12
|
+
[unreleased]: https://github.com/tillsteinbach/CarConnectivity-connector-skoda/compare/v0.1...HEAD
|
|
13
|
+
|
|
14
|
+
[0.1]: https://github.com/tillsteinbach/CarConnectivity-connector-skoda/releases/tag/v0.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: carconnectivity-connector-skoda
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2a1
|
|
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
|
|
40
|
+
Requires-Dist: carconnectivity>=0.2a1
|
|
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.2a1
|
|
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
|
|
40
|
+
Requires-Dist: carconnectivity>=0.2a1
|
|
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,5 +1,6 @@
|
|
|
1
1
|
.flake8
|
|
2
2
|
.gitignore
|
|
3
|
+
CHANGELOG.md
|
|
3
4
|
LICENSE
|
|
4
5
|
Makefile
|
|
5
6
|
README.md
|
|
@@ -22,6 +23,7 @@ src/carconnectivity_connectors/skoda/_version.py
|
|
|
22
23
|
src/carconnectivity_connectors/skoda/capability.py
|
|
23
24
|
src/carconnectivity_connectors/skoda/charging.py
|
|
24
25
|
src/carconnectivity_connectors/skoda/climatization.py
|
|
26
|
+
src/carconnectivity_connectors/skoda/command_impl.py
|
|
25
27
|
src/carconnectivity_connectors/skoda/connector.py
|
|
26
28
|
src/carconnectivity_connectors/skoda/error.py
|
|
27
29
|
src/carconnectivity_connectors/skoda/mqtt_client.py
|
|
@@ -89,12 +89,6 @@ class Capabilities(GenericObject):
|
|
|
89
89
|
"""
|
|
90
90
|
return capability_id in self.__capabilities
|
|
91
91
|
|
|
92
|
-
def __str__(self) -> str:
|
|
93
|
-
return_string = 'Capabilities:\n'
|
|
94
|
-
for capability in self.__capabilities.values():
|
|
95
|
-
return_string += f'\t{capability}\n'
|
|
96
|
-
return return_string
|
|
97
|
-
|
|
98
92
|
|
|
99
93
|
class Capability(GenericObject):
|
|
100
94
|
"""
|
|
@@ -113,10 +107,6 @@ class Capability(GenericObject):
|
|
|
113
107
|
self.enabled = True
|
|
114
108
|
self.delay_notifications = False
|
|
115
109
|
|
|
116
|
-
def __str__(self) -> str:
|
|
117
|
-
return_string = f'{self.capability_id}'
|
|
118
|
-
return return_string
|
|
119
|
-
|
|
120
110
|
class Status(IntEnum):
|
|
121
111
|
"""
|
|
122
112
|
Enum for capability status.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""This module defines the classes that represent attributes in the CarConnectivity system."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from typing import TYPE_CHECKING, Dict, Union
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
import argparse
|
|
7
|
+
import logging
|
|
8
|
+
|
|
9
|
+
from carconnectivity.commands import GenericCommand
|
|
10
|
+
from carconnectivity.objects import GenericObject
|
|
11
|
+
from carconnectivity.errors import SetterError
|
|
12
|
+
from carconnectivity.util import ThrowingArgumentParser
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from carconnectivity.objects import Optional
|
|
16
|
+
|
|
17
|
+
LOG: logging.Logger = logging.getLogger("carconnectivity.connectors.skoda")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SpinCommand(GenericCommand):
|
|
21
|
+
"""
|
|
22
|
+
SpinCommand is a command class for verifying the spin
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
def __init__(self, name: str = 'spin', parent: Optional[GenericObject] = None) -> None:
|
|
26
|
+
super().__init__(name=name, parent=parent)
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def value(self) -> Optional[Union[str, Dict]]:
|
|
30
|
+
return super().value
|
|
31
|
+
|
|
32
|
+
@value.setter
|
|
33
|
+
def value(self, new_value: Optional[Union[str, Dict]]) -> None:
|
|
34
|
+
if isinstance(new_value, str):
|
|
35
|
+
parser = ThrowingArgumentParser(prog='', add_help=False, exit_on_error=False)
|
|
36
|
+
parser.add_argument('command', help='Command to execute', type=SpinCommand.Command,
|
|
37
|
+
choices=list(SpinCommand.Command))
|
|
38
|
+
parser.add_argument('--spin', dest='spin', help='Spin to be used instead of spin from config or .netrc', type=str, required=False,
|
|
39
|
+
default=None)
|
|
40
|
+
try:
|
|
41
|
+
args = parser.parse_args(new_value.split(sep=' '))
|
|
42
|
+
except argparse.ArgumentError as e:
|
|
43
|
+
raise SetterError(f'Invalid format for SpinCommand: {e.message} {parser.format_usage()}') from e
|
|
44
|
+
|
|
45
|
+
newvalue_dict = {}
|
|
46
|
+
newvalue_dict['command'] = args.command
|
|
47
|
+
if args.spin is not None:
|
|
48
|
+
newvalue_dict['spin'] = args.spin
|
|
49
|
+
new_value = newvalue_dict
|
|
50
|
+
elif isinstance(new_value, dict):
|
|
51
|
+
if 'command' in new_value and isinstance(new_value['command'], str):
|
|
52
|
+
if new_value['command'] in SpinCommand.Command:
|
|
53
|
+
new_value['command'] = SpinCommand.Command(new_value['command'])
|
|
54
|
+
else:
|
|
55
|
+
raise ValueError('Invalid value for SpinCommand. '
|
|
56
|
+
f'Command must be one of {SpinCommand.Command}')
|
|
57
|
+
if self._is_changeable:
|
|
58
|
+
for hook in self._on_set_hooks:
|
|
59
|
+
new_value = hook(self, new_value)
|
|
60
|
+
self._set_value(new_value)
|
|
61
|
+
else:
|
|
62
|
+
raise TypeError('You cannot set this attribute. Attribute is not mutable.')
|
|
63
|
+
|
|
64
|
+
class Command(Enum):
|
|
65
|
+
"""
|
|
66
|
+
Enum class representing different commands for SPIN.
|
|
67
|
+
|
|
68
|
+
"""
|
|
69
|
+
VERIFY = 'verify'
|
|
70
|
+
|
|
71
|
+
def __str__(self) -> str:
|
|
72
|
+
return self.value
|
|
@@ -15,7 +15,7 @@ import requests
|
|
|
15
15
|
from carconnectivity.garage import Garage
|
|
16
16
|
from carconnectivity.vehicle import GenericVehicle
|
|
17
17
|
from carconnectivity.errors import AuthenticationError, TooManyRequestsError, RetrievalError, APIError, APICompatibilityError, \
|
|
18
|
-
TemporaryAuthenticationError, ConfigurationError, SetterError
|
|
18
|
+
TemporaryAuthenticationError, ConfigurationError, SetterError, CommandError
|
|
19
19
|
from carconnectivity.util import robust_time_parse, log_extra_keys, config_remove_credentials
|
|
20
20
|
from carconnectivity.units import Length, Speed, Power, Temperature
|
|
21
21
|
from carconnectivity.doors import Doors
|
|
@@ -27,7 +27,8 @@ from carconnectivity.charging import Charging
|
|
|
27
27
|
from carconnectivity.position import Position
|
|
28
28
|
from carconnectivity.climatization import Climatization
|
|
29
29
|
from carconnectivity.charging_connector import ChargingConnector
|
|
30
|
-
from carconnectivity.
|
|
30
|
+
from carconnectivity.commands import Commands
|
|
31
|
+
from carconnectivity.command_impl import ClimatizationStartStopCommand, ChargingStartStopCommand, HonkAndFlashCommand, LockUnlockCommand, WakeSleepCommand
|
|
31
32
|
|
|
32
33
|
from carconnectivity_connectors.base.connector import BaseConnector
|
|
33
34
|
from carconnectivity_connectors.skoda.auth.session_manager import SessionManager, SessionUser, Service
|
|
@@ -39,6 +40,7 @@ from carconnectivity_connectors.skoda.climatization import SkodaClimatization
|
|
|
39
40
|
from carconnectivity_connectors.skoda.error import Error
|
|
40
41
|
from carconnectivity_connectors.skoda._version import __version__
|
|
41
42
|
from carconnectivity_connectors.skoda.mqtt_client import SkodaMQTTClient
|
|
43
|
+
from carconnectivity_connectors.skoda.command_impl import SpinCommand
|
|
42
44
|
|
|
43
45
|
if TYPE_CHECKING:
|
|
44
46
|
from typing import Dict, List, Optional, Any, Set, Union
|
|
@@ -69,6 +71,7 @@ class Connector(BaseConnector):
|
|
|
69
71
|
|
|
70
72
|
self.connected: BooleanAttribute = BooleanAttribute(name="connected", parent=self)
|
|
71
73
|
self.interval: DurationAttribute = DurationAttribute(name="interval", parent=self)
|
|
74
|
+
self.commands: Commands = Commands(parent=self)
|
|
72
75
|
|
|
73
76
|
self.user_id: Optional[str] = None
|
|
74
77
|
|
|
@@ -91,6 +94,11 @@ class Connector(BaseConnector):
|
|
|
91
94
|
raise ConfigurationError(f'Invalid log level: "{config["log_level"]}" not in {list(logging._nameToLevel.keys())}')
|
|
92
95
|
LOG.info("Loading skoda connector with config %s", config_remove_credentials(self.config))
|
|
93
96
|
|
|
97
|
+
if 'spin' in config and config['spin'] is not None:
|
|
98
|
+
self._spin: Optional[str] = config['spin']
|
|
99
|
+
else:
|
|
100
|
+
self._spin = None
|
|
101
|
+
|
|
94
102
|
username: Optional[str] = None
|
|
95
103
|
password: Optional[str] = None
|
|
96
104
|
if 'username' in self.config and 'password' in self.config:
|
|
@@ -106,7 +114,13 @@ class Connector(BaseConnector):
|
|
|
106
114
|
secret: tuple[str, str, str] | None = secrets.authenticators("skoda")
|
|
107
115
|
if secret is None:
|
|
108
116
|
raise AuthenticationError(f'Authentication using {netrc_filename} failed: skoda not found in netrc')
|
|
109
|
-
username,
|
|
117
|
+
username, account, password = secret
|
|
118
|
+
|
|
119
|
+
if self._spin is None and account is not None:
|
|
120
|
+
try:
|
|
121
|
+
self._spin = account
|
|
122
|
+
except ValueError as err:
|
|
123
|
+
LOG.error('Could not parse spin from netrc: %s', err)
|
|
110
124
|
except netrc.NetrcParseError as err:
|
|
111
125
|
LOG.error('Authentification using %s failed: %s', netrc_filename, err)
|
|
112
126
|
raise AuthenticationError(f'Authentication using {netrc_filename} failed: {err}') from err
|
|
@@ -241,6 +255,12 @@ class Connector(BaseConnector):
|
|
|
241
255
|
|
|
242
256
|
This method calls the `fetch_vehicles` method to retrieve vehicle data.
|
|
243
257
|
"""
|
|
258
|
+
# Add spin command
|
|
259
|
+
if self.commands is not None and not self.commands.contains_command('spin'):
|
|
260
|
+
spin_command = SpinCommand(parent=self.commands)
|
|
261
|
+
spin_command._add_on_set_hook(self.__on_spin) # pylint: disable=protected-access
|
|
262
|
+
spin_command.enabled = True
|
|
263
|
+
self.commands.add_command(spin_command)
|
|
244
264
|
self.fetch_vehicles()
|
|
245
265
|
self.car_connectivity.transaction_end()
|
|
246
266
|
|
|
@@ -348,10 +368,11 @@ class Connector(BaseConnector):
|
|
|
348
368
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}'
|
|
349
369
|
data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
|
350
370
|
if data is not None:
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
371
|
+
if not vehicle.climatization.commands.contains_command('start-stop'):
|
|
372
|
+
start_stop_command: ChargingStartStopCommand = ChargingStartStopCommand(parent=vehicle.charging.commands)
|
|
373
|
+
start_stop_command._add_on_set_hook(self.__on_charging_start_stop) # pylint: disable=protected-access
|
|
374
|
+
start_stop_command.enabled = True
|
|
375
|
+
vehicle.charging.commands.add_command(start_stop_command)
|
|
355
376
|
if 'carCapturedTimestamp' in data and data['carCapturedTimestamp'] is not None:
|
|
356
377
|
captured_at: datetime = robust_time_parse(data['carCapturedTimestamp'])
|
|
357
378
|
else:
|
|
@@ -872,6 +893,32 @@ class Connector(BaseConnector):
|
|
|
872
893
|
else:
|
|
873
894
|
vehicle.capabilities.clear_capabilities()
|
|
874
895
|
|
|
896
|
+
if vehicle.capabilities.has_capability('VEHICLE_WAKE_UP_TRIGGER'):
|
|
897
|
+
if vehicle.commands is not None and vehicle.commands.commands is not None \
|
|
898
|
+
and not vehicle.commands.contains_command('wake-sleep'):
|
|
899
|
+
wake_sleep_command = WakeSleepCommand(parent=vehicle.commands)
|
|
900
|
+
wake_sleep_command._add_on_set_hook(self.__on_wake_sleep) # pylint: disable=protected-access
|
|
901
|
+
wake_sleep_command.enabled = True
|
|
902
|
+
vehicle.commands.add_command(wake_sleep_command)
|
|
903
|
+
|
|
904
|
+
# Add HONK_AND_FLASH command if necessary capabilities are available
|
|
905
|
+
if vehicle.capabilities.has_capability('HONK_AND_FLASH') and vehicle.capabilities.has_capability('PARKING_POSITION'):
|
|
906
|
+
if vehicle.commands is not None and vehicle.commands.commands is not None \
|
|
907
|
+
and not vehicle.commands.contains_command('honk-flash'):
|
|
908
|
+
honk_flash_command = HonkAndFlashCommand(parent=vehicle.commands)
|
|
909
|
+
honk_flash_command._add_on_set_hook(self.__on_honk_flash) # pylint: disable=protected-access
|
|
910
|
+
honk_flash_command.enabled = True
|
|
911
|
+
vehicle.commands.add_command(honk_flash_command)
|
|
912
|
+
|
|
913
|
+
# Add lock and unlock command
|
|
914
|
+
if vehicle.capabilities.has_capability('ACCESS'):
|
|
915
|
+
if vehicle.doors is not None and vehicle.doors.commands is not None and vehicle.doors.commands.commands is not None \
|
|
916
|
+
and not vehicle.doors.commands.contains_command('lock-unlock'):
|
|
917
|
+
lock_unlock_command = LockUnlockCommand(parent=vehicle.doors.commands)
|
|
918
|
+
lock_unlock_command._add_on_set_hook(self.__on_lock_unlock) # pylint: disable=protected-access
|
|
919
|
+
lock_unlock_command.enabled = True
|
|
920
|
+
vehicle.doors.commands.add_command(lock_unlock_command)
|
|
921
|
+
|
|
875
922
|
if 'specification' in vehicle_data and vehicle_data['specification'] is not None:
|
|
876
923
|
if 'model' in vehicle_data['specification'] and vehicle_data['specification']['model'] is not None:
|
|
877
924
|
vehicle.model._set_value(vehicle_data['specification']['model']) # pylint: disable=protected-access
|
|
@@ -1087,187 +1134,6 @@ class Connector(BaseConnector):
|
|
|
1087
1134
|
log_extra_keys(LOG_API, f'/api/v2/vehicle-status/{vin}', vehicle_status_data, {'overall', 'carCapturedTimestamp'})
|
|
1088
1135
|
return vehicle
|
|
1089
1136
|
|
|
1090
|
-
# def fetch_vehicle_status_second_api(self, vehicle: SkodaVehicle, no_cache: bool = False) -> SkodaVehicle:
|
|
1091
|
-
# """
|
|
1092
|
-
# Fetches the status of a vehicle from other Skoda API.
|
|
1093
|
-
#
|
|
1094
|
-
# Args:
|
|
1095
|
-
# vehicle (GenericVehicle): The vehicle object containing the VIN.
|
|
1096
|
-
#
|
|
1097
|
-
# Returns:
|
|
1098
|
-
# None
|
|
1099
|
-
# """
|
|
1100
|
-
# vin = vehicle.vin.value
|
|
1101
|
-
# if vin is None:
|
|
1102
|
-
# raise APIError('VIN is missing')
|
|
1103
|
-
# url = f'https://api.connect.skoda-auto.cz/api/v2/vehicle-status/{vin}'
|
|
1104
|
-
# vehicle_status_data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
|
1105
|
-
# if vehicle_status_data:
|
|
1106
|
-
# if 'remote' in vehicle_status_data and vehicle_status_data['remote'] is not None:
|
|
1107
|
-
# vehicle_status_data = vehicle_status_data['remote']
|
|
1108
|
-
# if vehicle_status_data:
|
|
1109
|
-
# if 'capturedAt' in vehicle_status_data and vehicle_status_data['capturedAt'] is not None:
|
|
1110
|
-
# captured_at: datetime = robust_time_parse(vehicle_status_data['capturedAt'])
|
|
1111
|
-
# else:
|
|
1112
|
-
# raise APIError('Could not fetch vehicle status, capturedAt missing')
|
|
1113
|
-
# if 'mileageInKm' in vehicle_status_data and vehicle_status_data['mileageInKm'] is not None:
|
|
1114
|
-
# # pylint: disable-next=protected-access
|
|
1115
|
-
# vehicle.odometer._set_value(value=vehicle_status_data['mileageInKm'], measured=captured_at, unit=Length.KM)
|
|
1116
|
-
# else:
|
|
1117
|
-
# vehicle.odometer._set_value(value=None, measured=captured_at, unit=Length.KM) # pylint: disable=protected-access
|
|
1118
|
-
# if 'status' in vehicle_status_data and vehicle_status_data['status'] is not None:
|
|
1119
|
-
# if 'open' in vehicle_status_data['status'] and vehicle_status_data['status']['open'] is not None:
|
|
1120
|
-
# if vehicle_status_data['status']['open'] == 'YES':
|
|
1121
|
-
# vehicle.doors.open_state._set_value(Doors.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
|
|
1122
|
-
# elif vehicle_status_data['status']['open'] == 'NO':
|
|
1123
|
-
# vehicle.doors.open_state._set_value(Doors.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
|
|
1124
|
-
# else:
|
|
1125
|
-
# vehicle.doors.open_state._set_value(Doors.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1126
|
-
# LOG_API.info('Unknown door open state: %s', vehicle_status_data['status']['open'])
|
|
1127
|
-
# else:
|
|
1128
|
-
# vehicle.doors.open_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
|
|
1129
|
-
# if 'locked' in vehicle_status_data['status'] and vehicle_status_data['status']['locked'] is not None:
|
|
1130
|
-
# if vehicle_status_data['status']['locked'] == 'YES':
|
|
1131
|
-
# vehicle.doors.lock_state._set_value(Doors.LockState.LOCKED, measured=captured_at) # pylint: disable=protected-access
|
|
1132
|
-
# elif vehicle_status_data['status']['locked'] == 'NO':
|
|
1133
|
-
# vehicle.doors.lock_state._set_value(Doors.LockState.UNLOCKED, measured=captured_at) # pylint: disable=protected-access
|
|
1134
|
-
# else:
|
|
1135
|
-
# vehicle.doors.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1136
|
-
# LOG_API.info('Unknown door lock state: %s', vehicle_status_data['status']['locked'])
|
|
1137
|
-
# else:
|
|
1138
|
-
# vehicle.doors.lock_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
|
|
1139
|
-
# else:
|
|
1140
|
-
# vehicle.doors.open_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
|
|
1141
|
-
# vehicle.doors.lock_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
|
|
1142
|
-
# if 'doors' in vehicle_status_data and vehicle_status_data['doors'] is not None:
|
|
1143
|
-
# seen_door_ids: set[str] = set()
|
|
1144
|
-
# for door_status in vehicle_status_data['doors']:
|
|
1145
|
-
# if 'name' in door_status and door_status['name'] is not None:
|
|
1146
|
-
# door_id = door_status['name']
|
|
1147
|
-
# seen_door_ids.add(door_id)
|
|
1148
|
-
# if door_id in vehicle.doors.doors:
|
|
1149
|
-
# door: Doors.Door = vehicle.doors.doors[door_id]
|
|
1150
|
-
# else:
|
|
1151
|
-
# door = Doors.Door(door_id=door_id, doors=vehicle.doors)
|
|
1152
|
-
# vehicle.doors.doors[door_id] = door
|
|
1153
|
-
# if 'status' in door_status and door_status['status'] is not None:
|
|
1154
|
-
# if door_status['status'] == 'OPEN':
|
|
1155
|
-
# door.lock_state._set_value(Doors.LockState.UNLOCKED, measured=captured_at) # pylint: disable=protected-access
|
|
1156
|
-
# door.open_state._set_value(Doors.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
|
|
1157
|
-
# elif door_status['status'] == 'CLOSED':
|
|
1158
|
-
# door.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1159
|
-
# door.open_state._set_value(Doors.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
|
|
1160
|
-
# elif door_status['status'] == 'LOCKED':
|
|
1161
|
-
# door.lock_state._set_value(Doors.LockState.LOCKED, measured=captured_at) # pylint: disable=protected-access
|
|
1162
|
-
# door.open_state._set_value(Doors.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
|
|
1163
|
-
# elif door_status['status'] == 'UNSUPPORTED':
|
|
1164
|
-
# door.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1165
|
-
# door.open_state._set_value(Doors.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1166
|
-
# else:
|
|
1167
|
-
# LOG_API.info('Unknown door status %s', door_status['status'])
|
|
1168
|
-
# door.lock_state._set_value(Doors.LockState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1169
|
-
# door.open_state._set_value(Doors.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1170
|
-
# else:
|
|
1171
|
-
# door.lock_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
|
|
1172
|
-
# door.open_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
|
|
1173
|
-
# else:
|
|
1174
|
-
# raise APIError('Could not parse door, name missing')
|
|
1175
|
-
# log_extra_keys(LOG_API, 'doors', door_status, {'name', 'status'})
|
|
1176
|
-
# for door_to_remove in set(vehicle.doors.doors) - seen_door_ids:
|
|
1177
|
-
# vehicle.doors.doors[door_to_remove].enabled = False
|
|
1178
|
-
# vehicle.doors.doors.pop(door_to_remove)
|
|
1179
|
-
# log_extra_keys(LOG_API, 'status', vehicle_status_data['status'], {'open', 'locked'})
|
|
1180
|
-
# else:
|
|
1181
|
-
# vehicle.doors.open_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
|
|
1182
|
-
# vehicle.doors.doors = {}
|
|
1183
|
-
# if 'windows' in vehicle_status_data and vehicle_status_data['windows'] is not None:
|
|
1184
|
-
# seen_window_ids: set[str] = set()
|
|
1185
|
-
# all_windows_closed: bool = True
|
|
1186
|
-
# for window_status in vehicle_status_data['windows']:
|
|
1187
|
-
# if 'name' in window_status and window_status['name'] is not None:
|
|
1188
|
-
# window_id = window_status['name']
|
|
1189
|
-
# seen_window_ids.add(window_id)
|
|
1190
|
-
# if window_id in vehicle.windows.windows:
|
|
1191
|
-
# window: Windows.Window = vehicle.windows.windows[window_id]
|
|
1192
|
-
# else:
|
|
1193
|
-
# window = Windows.Window(window_id=window_id, windows=vehicle.windows)
|
|
1194
|
-
# vehicle.windows.windows[window_id] = window
|
|
1195
|
-
# if 'status' in window_status and window_status['status'] is not None:
|
|
1196
|
-
# if window_status['status'] == 'OPEN':
|
|
1197
|
-
# all_windows_closed = False
|
|
1198
|
-
# window.open_state._set_value(Windows.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
|
|
1199
|
-
# elif window_status['status'] == 'CLOSED':
|
|
1200
|
-
# window.open_state._set_value(Windows.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
|
|
1201
|
-
# elif window_status['status'] == 'UNSUPPORTED':
|
|
1202
|
-
# window.open_state._set_value(Windows.OpenState.UNSUPPORTED, measured=captured_at) # pylint: disable=protected-access
|
|
1203
|
-
# elif window_status['status'] == 'INVALID':
|
|
1204
|
-
# window.open_state._set_value(Windows.OpenState.INVALID, measured=captured_at) # pylint: disable=protected-access
|
|
1205
|
-
# else:
|
|
1206
|
-
# LOG_API.info('Unknown window status %s', window_status['status'])
|
|
1207
|
-
# window.open_state._set_value(Windows.OpenState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1208
|
-
# else:
|
|
1209
|
-
# window.open_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
|
|
1210
|
-
# else:
|
|
1211
|
-
# raise APIError('Could not parse window, name missing')
|
|
1212
|
-
# log_extra_keys(LOG_API, 'doors', window_status, {'name', 'status'})
|
|
1213
|
-
# for window_to_remove in set(vehicle.windows.windows) - seen_window_ids:
|
|
1214
|
-
# vehicle.windows.windows[window_to_remove].enabled = False
|
|
1215
|
-
# vehicle.windows.windows.pop(window_to_remove)
|
|
1216
|
-
# if all_windows_closed:
|
|
1217
|
-
# vehicle.windows.open_state._set_value(Windows.OpenState.CLOSED, measured=captured_at) # pylint: disable=protected-access
|
|
1218
|
-
# else:
|
|
1219
|
-
# vehicle.windows.open_state._set_value(Windows.OpenState.OPEN, measured=captured_at) # pylint: disable=protected-access
|
|
1220
|
-
# else:
|
|
1221
|
-
# vehicle.windows.open_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
|
|
1222
|
-
# vehicle.windows.windows = {}
|
|
1223
|
-
# if 'lights' in vehicle_status_data and vehicle_status_data['lights'] is not None:
|
|
1224
|
-
# seen_light_ids: set[str] = set()
|
|
1225
|
-
# if 'overallStatus' in vehicle_status_data['lights'] and vehicle_status_data['lights']['overallStatus'] is not None:
|
|
1226
|
-
# if vehicle_status_data['lights']['overallStatus'] == 'ON':
|
|
1227
|
-
# vehicle.lights.light_state._set_value(Lights.LightState.ON, measured=captured_at) # pylint: disable=protected-access
|
|
1228
|
-
# elif vehicle_status_data['lights']['overallStatus'] == 'OFF':
|
|
1229
|
-
# vehicle.lights.light_state._set_value(Lights.LightState.OFF, measured=captured_at) # pylint: disable=protected-access
|
|
1230
|
-
# elif vehicle_status_data['lights']['overallStatus'] == 'INVALID':
|
|
1231
|
-
# vehicle.lights.light_state._set_value(Lights.LightState.INVALID, measured=captured_at) # pylint: disable=protected-access
|
|
1232
|
-
# else:
|
|
1233
|
-
# LOG_API.info('Unknown light status %s', vehicle_status_data['lights']['overallStatus'])
|
|
1234
|
-
# vehicle.lights.light_state._set_value(Lights.LightState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1235
|
-
# else:
|
|
1236
|
-
# vehicle.lights.light_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
|
|
1237
|
-
# if 'lightsStatus' in vehicle_status_data['lights'] and vehicle_status_data['lights']['lightsStatus'] is not None:
|
|
1238
|
-
# for light_status in vehicle_status_data['lights']['lightsStatus']:
|
|
1239
|
-
# if 'name' in light_status and light_status['name'] is not None:
|
|
1240
|
-
# light_id: str = light_status['name']
|
|
1241
|
-
# seen_light_ids.add(light_id)
|
|
1242
|
-
# if light_id in vehicle.lights.lights:
|
|
1243
|
-
# light: Lights.Light = vehicle.lights.lights[light_id]
|
|
1244
|
-
# else:
|
|
1245
|
-
# light = Lights.Light(light_id=light_id, lights=vehicle.lights)
|
|
1246
|
-
# vehicle.lights.lights[light_id] = light
|
|
1247
|
-
# if 'status' in light_status and light_status['status'] is not None:
|
|
1248
|
-
# if light_status['status'] == 'ON':
|
|
1249
|
-
# light.light_state._set_value(Lights.LightState.ON, measured=captured_at) # pylint: disable=protected-access
|
|
1250
|
-
# elif light_status['status'] == 'OFF':
|
|
1251
|
-
# light.light_state._set_value(Lights.LightState.OFF, measured=captured_at) # pylint: disable=protected-access
|
|
1252
|
-
# elif light_status['status'] == 'INVALID':
|
|
1253
|
-
# light.light_state._set_value(Lights.LightState.INVALID, measured=captured_at) # pylint: disable=protected-access
|
|
1254
|
-
# else:
|
|
1255
|
-
# LOG_API.info('Unknown light status %s', light_status['status'])
|
|
1256
|
-
# light.light_state._set_value(Lights.LightState.UNKNOWN, measured=captured_at) # pylint: disable=protected-access
|
|
1257
|
-
# else:
|
|
1258
|
-
# light.light_state._set_value(None, measured=captured_at) # pylint: disable=protected-access
|
|
1259
|
-
# else:
|
|
1260
|
-
# raise APIError('Could not parse light, name missing')
|
|
1261
|
-
# log_extra_keys(LOG_API, 'lights', light_status, {'name', 'status'})
|
|
1262
|
-
# for light_to_remove in set(vehicle.lights.lights) - seen_light_ids:
|
|
1263
|
-
# vehicle.lights.lights[light_to_remove].enabled = False
|
|
1264
|
-
# vehicle.lights.lights.pop(light_to_remove)
|
|
1265
|
-
# else:
|
|
1266
|
-
# vehicle.lights.lights = {}
|
|
1267
|
-
# log_extra_keys(LOG_API, 'lights', vehicle_status_data['lights'], {'overallStatus', 'lightsStatus'})
|
|
1268
|
-
# log_extra_keys(LOG_API, 'vehicles', vehicle_status_data, {'capturedAt', 'mileageInKm', 'status', 'doors', 'windows', 'lights'})
|
|
1269
|
-
# return vehicle
|
|
1270
|
-
|
|
1271
1137
|
def _record_elapsed(self, elapsed: timedelta) -> None:
|
|
1272
1138
|
"""
|
|
1273
1139
|
Records the elapsed time.
|
|
@@ -1404,15 +1270,15 @@ class Connector(BaseConnector):
|
|
|
1404
1270
|
-> Union[str, Dict[str, Any]]:
|
|
1405
1271
|
if start_stop_command.parent is None or start_stop_command.parent.parent is None \
|
|
1406
1272
|
or start_stop_command.parent.parent.parent is None or not isinstance(start_stop_command.parent.parent.parent, SkodaVehicle):
|
|
1407
|
-
raise
|
|
1273
|
+
raise CommandError('Object hierarchy is not as expected')
|
|
1408
1274
|
if not isinstance(command_arguments, dict):
|
|
1409
|
-
raise
|
|
1275
|
+
raise CommandError('Command arguments are not a dictionary')
|
|
1410
1276
|
vehicle: SkodaVehicle = start_stop_command.parent.parent.parent
|
|
1411
1277
|
vin: Optional[str] = vehicle.vin.value
|
|
1412
1278
|
if vin is None:
|
|
1413
|
-
raise
|
|
1279
|
+
raise CommandError('VIN in object hierarchy missing')
|
|
1414
1280
|
if 'command' not in command_arguments:
|
|
1415
|
-
raise
|
|
1281
|
+
raise CommandError('Command argument missing')
|
|
1416
1282
|
command_dict = {}
|
|
1417
1283
|
if command_arguments['command'] == ClimatizationStartStopCommand.Command.START:
|
|
1418
1284
|
command_dict['heaterSource'] = 'ELECTRIC'
|
|
@@ -1422,7 +1288,7 @@ class Connector(BaseConnector):
|
|
|
1422
1288
|
command_dict['targetTemperature']['temperatureValue'] = round(command_arguments['target_temperature'] * 2) / 2
|
|
1423
1289
|
if 'target_temperature_unit' in command_arguments:
|
|
1424
1290
|
if not isinstance(command_arguments['target_temperature_unit'], Temperature):
|
|
1425
|
-
raise
|
|
1291
|
+
raise CommandError('Temperature unit is not of type Temperature')
|
|
1426
1292
|
if command_arguments['target_temperature_unit'] == Temperature.C:
|
|
1427
1293
|
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1428
1294
|
elif command_arguments['target_temperature_unit'] == Temperature.F:
|
|
@@ -1430,7 +1296,7 @@ class Connector(BaseConnector):
|
|
|
1430
1296
|
elif command_arguments['target_temperature_unit'] == Temperature.K:
|
|
1431
1297
|
command_dict['targetTemperature']['unitInCar'] = 'KELVIN'
|
|
1432
1298
|
else:
|
|
1433
|
-
raise
|
|
1299
|
+
raise CommandError(f'Unknown temperature unit {command_arguments['target_temperature_unit']}')
|
|
1434
1300
|
else:
|
|
1435
1301
|
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1436
1302
|
elif start_stop_command.parent is not None and (climatization := start_stop_command.parent.parent) is not None \
|
|
@@ -1446,46 +1312,167 @@ class Connector(BaseConnector):
|
|
|
1446
1312
|
elif climatization.settings.target_temperature.unit == Temperature.K:
|
|
1447
1313
|
command_dict['targetTemperature']['unitInCar'] = 'KELVIN'
|
|
1448
1314
|
else:
|
|
1449
|
-
raise
|
|
1315
|
+
raise CommandError(f'Unknown temperature unit {climatization.settings.target_temperature.unit}')
|
|
1450
1316
|
else:
|
|
1451
1317
|
command_dict['targetTemperature']['temperatureValue'] = 25.0
|
|
1452
1318
|
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1453
1319
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/start'
|
|
1454
|
-
|
|
1320
|
+
command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
|
1455
1321
|
elif command_arguments['command'] == ClimatizationStartStopCommand.Command.STOP:
|
|
1456
1322
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/stop'
|
|
1457
|
-
|
|
1323
|
+
command_response: requests.Response = self.session.post(url, allow_redirects=True)
|
|
1458
1324
|
else:
|
|
1459
|
-
raise
|
|
1325
|
+
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
|
1460
1326
|
|
|
1461
|
-
if
|
|
1462
|
-
LOG.error('Could not start/stop air conditioning (%s: %s)',
|
|
1463
|
-
raise
|
|
1327
|
+
if command_response.status_code != requests.codes['accepted']:
|
|
1328
|
+
LOG.error('Could not start/stop air conditioning (%s: %s)', command_response.status_code, command_response.text)
|
|
1329
|
+
raise CommandError(f'Could not start/stop air conditioning ({command_response.status_code}: {command_response.text})')
|
|
1464
1330
|
return command_arguments
|
|
1465
1331
|
|
|
1466
1332
|
def __on_charging_start_stop(self, start_stop_command: ChargingStartStopCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
|
1467
1333
|
-> Union[str, Dict[str, Any]]:
|
|
1468
1334
|
if start_stop_command.parent is None or start_stop_command.parent.parent is None \
|
|
1469
1335
|
or start_stop_command.parent.parent.parent is None or not isinstance(start_stop_command.parent.parent.parent, SkodaVehicle):
|
|
1470
|
-
raise
|
|
1336
|
+
raise CommandError('Object hierarchy is not as expected')
|
|
1471
1337
|
if not isinstance(command_arguments, dict):
|
|
1472
|
-
raise
|
|
1338
|
+
raise CommandError('Command arguments are not a dictionary')
|
|
1473
1339
|
vehicle: SkodaVehicle = start_stop_command.parent.parent.parent
|
|
1474
1340
|
vin: Optional[str] = vehicle.vin.value
|
|
1475
1341
|
if vin is None:
|
|
1476
|
-
raise
|
|
1342
|
+
raise CommandError('VIN in object hierarchy missing')
|
|
1477
1343
|
if 'command' not in command_arguments:
|
|
1478
|
-
raise
|
|
1344
|
+
raise CommandError('Command argument missing')
|
|
1479
1345
|
if command_arguments['command'] == ChargingStartStopCommand.Command.START:
|
|
1480
1346
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}/start'
|
|
1481
|
-
|
|
1347
|
+
command_response: requests.Response = self.session.post(url, allow_redirects=True)
|
|
1482
1348
|
elif command_arguments['command'] == ChargingStartStopCommand.Command.STOP:
|
|
1483
1349
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}/stop'
|
|
1484
|
-
|
|
1350
|
+
command_response: requests.Response = self.session.post(url, allow_redirects=True)
|
|
1485
1351
|
else:
|
|
1486
|
-
raise
|
|
1352
|
+
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
|
1487
1353
|
|
|
1488
|
-
if
|
|
1489
|
-
LOG.error('Could not start/stop charging (%s: %s)',
|
|
1490
|
-
raise
|
|
1354
|
+
if command_response.status_code != requests.codes['accepted']:
|
|
1355
|
+
LOG.error('Could not start/stop charging (%s: %s)', command_response.status_code, command_response.text)
|
|
1356
|
+
raise CommandError(f'Could not start/stop charging ({command_response.status_code}: {command_response.text})')
|
|
1357
|
+
return command_arguments
|
|
1358
|
+
|
|
1359
|
+
def __on_honk_flash(self, honk_flash_command: HonkAndFlashCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
|
1360
|
+
-> Union[str, Dict[str, Any]]:
|
|
1361
|
+
if honk_flash_command.parent is None or honk_flash_command.parent.parent is None \
|
|
1362
|
+
or not isinstance(honk_flash_command.parent.parent, SkodaVehicle):
|
|
1363
|
+
raise CommandError('Object hierarchy is not as expected')
|
|
1364
|
+
if not isinstance(command_arguments, dict):
|
|
1365
|
+
raise CommandError('Command arguments are not a dictionary')
|
|
1366
|
+
vehicle: SkodaVehicle = honk_flash_command.parent.parent
|
|
1367
|
+
vin: Optional[str] = vehicle.vin.value
|
|
1368
|
+
if vin is None:
|
|
1369
|
+
raise CommandError('VIN in object hierarchy missing')
|
|
1370
|
+
if 'command' not in command_arguments:
|
|
1371
|
+
raise CommandError('Command argument missing')
|
|
1372
|
+
if 'duration' in command_arguments:
|
|
1373
|
+
LOG.warning('Duration argument is not supported by the Skoda API')
|
|
1374
|
+
command_dict = {}
|
|
1375
|
+
if command_arguments['command'] in [HonkAndFlashCommand.Command.FLASH, HonkAndFlashCommand.Command.HONK_AND_FLASH]:
|
|
1376
|
+
command_dict['mode'] = command_arguments['command'].name
|
|
1377
|
+
command_dict['vehiclePosition'] = {}
|
|
1378
|
+
if vehicle.position is None or vehicle.position.latitude is None or vehicle.position.longitude is None \
|
|
1379
|
+
or vehicle.position.latitude.value is None or vehicle.position.longitude.value is None \
|
|
1380
|
+
or not vehicle.position.latitude.enabled or not vehicle.position.longitude.enabled:
|
|
1381
|
+
raise CommandError('Can only execute honk and flash commands if vehicle position is known')
|
|
1382
|
+
command_dict['vehiclePosition']['latitude'] = vehicle.position.latitude.value
|
|
1383
|
+
command_dict['vehiclePosition']['longitude'] = vehicle.position.longitude.value
|
|
1384
|
+
|
|
1385
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/vehicle-access/{vin}/honk-and-flash'
|
|
1386
|
+
command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
|
1387
|
+
if command_response.status_code != requests.codes['accepted']:
|
|
1388
|
+
LOG.error('Could not execute honk or flash command (%s: %s)', command_response.status_code, command_response.text)
|
|
1389
|
+
raise CommandError(f'Could not execute honk or flash command ({command_response.status_code}: {command_response.text})')
|
|
1390
|
+
else:
|
|
1391
|
+
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
|
1392
|
+
return command_arguments
|
|
1393
|
+
|
|
1394
|
+
def __on_lock_unlock(self, lock_unlock_command: LockUnlockCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
|
1395
|
+
-> Union[str, Dict[str, Any]]:
|
|
1396
|
+
if lock_unlock_command.parent is None or lock_unlock_command.parent.parent is None \
|
|
1397
|
+
or lock_unlock_command.parent.parent.parent is None or not isinstance(lock_unlock_command.parent.parent.parent, SkodaVehicle):
|
|
1398
|
+
raise CommandError('Object hierarchy is not as expected')
|
|
1399
|
+
if not isinstance(command_arguments, dict):
|
|
1400
|
+
raise SetterError('Command arguments are not a dictionary')
|
|
1401
|
+
vehicle: SkodaVehicle = lock_unlock_command.parent.parent.parent
|
|
1402
|
+
vin: Optional[str] = vehicle.vin.value
|
|
1403
|
+
if vin is None:
|
|
1404
|
+
raise CommandError('VIN in object hierarchy missing')
|
|
1405
|
+
if 'command' not in command_arguments:
|
|
1406
|
+
raise CommandError('Command argument missing')
|
|
1407
|
+
command_dict = {}
|
|
1408
|
+
if 'spin' in command_arguments:
|
|
1409
|
+
command_dict['currentSpin'] = command_arguments['spin']
|
|
1410
|
+
else:
|
|
1411
|
+
if self._spin is None:
|
|
1412
|
+
raise CommandError('S-PIN is missing, please add S-PIN to your configuration or .netrc file')
|
|
1413
|
+
command_dict['currentSpin'] = self._spin
|
|
1414
|
+
if command_arguments['command'] == LockUnlockCommand.Command.LOCK:
|
|
1415
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/vehicle-access/{vin}/lock'
|
|
1416
|
+
elif command_arguments['command'] == LockUnlockCommand.Command.UNLOCK:
|
|
1417
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/vehicle-access/{vin}/unlock'
|
|
1418
|
+
else:
|
|
1419
|
+
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
|
1420
|
+
command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
|
1421
|
+
if command_response.status_code != requests.codes['accepted']:
|
|
1422
|
+
LOG.error('Could not execute locking command (%s: %s)', command_response.status_code, command_response.text)
|
|
1423
|
+
raise CommandError(f'Could not execute locking command ({command_response.status_code}: {command_response.text})')
|
|
1424
|
+
return command_arguments
|
|
1425
|
+
|
|
1426
|
+
def __on_wake_sleep(self, wake_sleep_command: WakeSleepCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
|
1427
|
+
-> Union[str, Dict[str, Any]]:
|
|
1428
|
+
if wake_sleep_command.parent is None or wake_sleep_command.parent.parent is None \
|
|
1429
|
+
or not isinstance(wake_sleep_command.parent.parent, GenericVehicle):
|
|
1430
|
+
raise CommandError('Object hierarchy is not as expected')
|
|
1431
|
+
if not isinstance(command_arguments, dict):
|
|
1432
|
+
raise CommandError('Command arguments are not a dictionary')
|
|
1433
|
+
vehicle: GenericVehicle = wake_sleep_command.parent.parent
|
|
1434
|
+
vin: Optional[str] = vehicle.vin.value
|
|
1435
|
+
if vin is None:
|
|
1436
|
+
raise CommandError('VIN in object hierarchy missing')
|
|
1437
|
+
if 'command' not in command_arguments:
|
|
1438
|
+
raise CommandError('Command argument missing')
|
|
1439
|
+
if command_arguments['command'] == WakeSleepCommand.Command.WAKE:
|
|
1440
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/vehicle-wakeup/{vin}?applyRequestLimiter=true'
|
|
1441
|
+
|
|
1442
|
+
command_response: requests.Response = self.session.post(url, data='{}', allow_redirects=True)
|
|
1443
|
+
if command_response.status_code != requests.codes['accepted']:
|
|
1444
|
+
LOG.error('Could not execute wake command (%s: %s)', command_response.status_code, command_response.text)
|
|
1445
|
+
raise CommandError(f'Could not execute wake command ({command_response.status_code}: {command_response.text})')
|
|
1446
|
+
elif command_arguments['command'] == WakeSleepCommand.Command.SLEEP:
|
|
1447
|
+
raise CommandError('Sleep command not supported by vehicle. Vehicle will put itself to sleep')
|
|
1448
|
+
else:
|
|
1449
|
+
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
|
1450
|
+
return command_arguments
|
|
1451
|
+
|
|
1452
|
+
def __on_spin(self, spin_command: SpinCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
|
1453
|
+
-> Union[str, Dict[str, Any]]:
|
|
1454
|
+
del spin_command
|
|
1455
|
+
if not isinstance(command_arguments, dict):
|
|
1456
|
+
raise CommandError('Command arguments are not a dictionary')
|
|
1457
|
+
if 'command' not in command_arguments:
|
|
1458
|
+
raise CommandError('Command argument missing')
|
|
1459
|
+
command_dict = {}
|
|
1460
|
+
if self._spin is None:
|
|
1461
|
+
raise CommandError('S-PIN is missing, please add S-PIN to your configuration or .netrc file')
|
|
1462
|
+
if 'spin' in command_arguments:
|
|
1463
|
+
command_dict['currentSpin'] = command_arguments['spin']
|
|
1464
|
+
else:
|
|
1465
|
+
if self._spin is None or self._spin == '':
|
|
1466
|
+
raise CommandError('S-PIN is missing, please add S-PIN to your configuration or .netrc file')
|
|
1467
|
+
command_dict['currentSpin'] = self._spin
|
|
1468
|
+
if command_arguments['command'] == SpinCommand.Command.VERIFY:
|
|
1469
|
+
url = 'https://mysmob.api.connect.skoda-auto.cz/api/v1/spin/verify'
|
|
1470
|
+
else:
|
|
1471
|
+
raise CommandError(f'Unknown command {command_arguments["command"]}')
|
|
1472
|
+
command_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
|
1473
|
+
if command_response.status_code != requests.codes['ok']:
|
|
1474
|
+
LOG.error('Could not execute spin command (%s: %s)', command_response.status_code, command_response.text)
|
|
1475
|
+
raise CommandError(f'Could not execute spin command ({command_response.status_code}: {command_response.text})')
|
|
1476
|
+
else:
|
|
1477
|
+
LOG.info('Spin verify command executed successfully')
|
|
1491
1478
|
return command_arguments
|
|
@@ -494,6 +494,8 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
494
494
|
# pylint: disable-next=protected-access
|
|
495
495
|
vehicle.charging.power._set_value(value=0, measured=measured_at, unit=Power.KW)
|
|
496
496
|
if 'soc' in data['data'] and data['data']['soc'] is not None:
|
|
497
|
+
if isinstance(data['data']['soc'], str):
|
|
498
|
+
data['data']['soc'] = int(data['data']['soc'])
|
|
497
499
|
electric_drive.level._set_value(measured=measured_at, value=data['data']['soc']) # pylint: disable=protected-access
|
|
498
500
|
if 'chargedRange' in data['data'] and data['data']['chargedRange'] is not None:
|
|
499
501
|
# pylint: disable-next=protected-access
|
|
@@ -29,10 +29,6 @@ class SkodaVehicle(GenericVehicle): # pylint: disable=too-many-instance-attribu
|
|
|
29
29
|
self.capabilities = Capabilities(vehicle=self)
|
|
30
30
|
self.manufacturer._set_value(value='Škoda') # pylint: disable=protected-access
|
|
31
31
|
|
|
32
|
-
def __str__(self) -> str:
|
|
33
|
-
return_string: str = f'\t{self.capabilities}\n'
|
|
34
|
-
return return_string
|
|
35
|
-
|
|
36
32
|
|
|
37
33
|
class SkodaElectricVehicle(ElectricVehicle, SkodaVehicle):
|
|
38
34
|
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{carconnectivity_connector_skoda-0.1a21 → carconnectivity_connector_skoda-0.2a1}/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
|