carconnectivity-connector-skoda 0.4a4__tar.gz → 0.4a6__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.4a4 → carconnectivity_connector_skoda-0.4a6}/PKG-INFO +2 -2
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/pyproject.toml +1 -1
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connector_skoda.egg-info/PKG-INFO +2 -2
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connector_skoda.egg-info/requires.txt +1 -1
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/_version.py +1 -1
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/command_impl.py +4 -2
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/connector.py +28 -10
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/mqtt_client.py +7 -1
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/.flake8 +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/.github/dependabot.yml +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/.github/workflows/build.yml +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/.github/workflows/build_and_publish.yml +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/.github/workflows/codeql-analysis.yml +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/.gitignore +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/CHANGELOG.md +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/LICENSE +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/Makefile +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/README.md +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/doc/Config.md +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/setup.cfg +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/setup_requirements.txt +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connector_skoda.egg-info/SOURCES.txt +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connector_skoda.egg-info/dependency_links.txt +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connector_skoda.egg-info/top_level.txt +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/__init__.py +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/auth/__init__.py +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/auth/auth_util.py +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/auth/helpers/blacklist_retry.py +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/auth/my_skoda_session.py +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/auth/openid_session.py +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/auth/session_manager.py +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/auth/skoda_web_session.py +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/capability.py +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/charging.py +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/climatization.py +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/error.py +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/ui/connector_ui.py +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/src/carconnectivity_connectors/skoda/vehicle.py +0 -0
- {carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/test/integration_test/carConnectivity.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: carconnectivity-connector-skoda
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4a6
|
|
4
4
|
Summary: CarConnectivity connector for Skoda services
|
|
5
5
|
Author: Till Steinbach
|
|
6
6
|
License: MIT License
|
|
@@ -37,7 +37,7 @@ Classifier: Topic :: Software Development :: Libraries
|
|
|
37
37
|
Requires-Python: >=3.8
|
|
38
38
|
Description-Content-Type: text/markdown
|
|
39
39
|
License-File: LICENSE
|
|
40
|
-
Requires-Dist: carconnectivity>=0.
|
|
40
|
+
Requires-Dist: carconnectivity>=0.4a10
|
|
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.4a6
|
|
4
4
|
Summary: CarConnectivity connector for Skoda services
|
|
5
5
|
Author: Till Steinbach
|
|
6
6
|
License: MIT License
|
|
@@ -37,7 +37,7 @@ Classifier: Topic :: Software Development :: Libraries
|
|
|
37
37
|
Requires-Python: >=3.8
|
|
38
38
|
Description-Content-Type: text/markdown
|
|
39
39
|
License-File: LICENSE
|
|
40
|
-
Requires-Dist: carconnectivity>=0.
|
|
40
|
+
Requires-Dist: carconnectivity>=0.4a10
|
|
41
41
|
Requires-Dist: oauthlib~=3.2.2
|
|
42
42
|
Requires-Dist: requests~=2.32.3
|
|
43
43
|
Requires-Dist: jwt~=1.3.1
|
|
@@ -31,6 +31,8 @@ class SpinCommand(GenericCommand):
|
|
|
31
31
|
|
|
32
32
|
@value.setter
|
|
33
33
|
def value(self, new_value: Optional[Union[str, Dict]]) -> None:
|
|
34
|
+
# Execute early hooks before parsing the value
|
|
35
|
+
new_value = self._execute_on_set_hook(new_value, early_hook=True)
|
|
34
36
|
if isinstance(new_value, str):
|
|
35
37
|
parser = ThrowingArgumentParser(prog='', add_help=False, exit_on_error=False)
|
|
36
38
|
parser.add_argument('command', help='Command to execute', type=SpinCommand.Command,
|
|
@@ -55,8 +57,8 @@ class SpinCommand(GenericCommand):
|
|
|
55
57
|
raise ValueError('Invalid value for SpinCommand. '
|
|
56
58
|
f'Command must be one of {SpinCommand.Command}')
|
|
57
59
|
if self._is_changeable:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
# Execute late hooks before setting the value
|
|
61
|
+
new_value = self._execute_on_set_hook(new_value, early_hook=False)
|
|
60
62
|
self._set_value(new_value)
|
|
61
63
|
else:
|
|
62
64
|
raise TypeError('You cannot set this attribute. Attribute is not mutable.')
|
|
@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING
|
|
|
4
4
|
|
|
5
5
|
import threading
|
|
6
6
|
import os
|
|
7
|
+
import traceback
|
|
7
8
|
import logging
|
|
8
9
|
import netrc
|
|
9
10
|
from datetime import datetime, timedelta, timezone
|
|
@@ -72,6 +73,7 @@ class Connector(BaseConnector):
|
|
|
72
73
|
"""
|
|
73
74
|
def __init__(self, connector_id: str, car_connectivity: CarConnectivity, config: Dict) -> None:
|
|
74
75
|
BaseConnector.__init__(self, connector_id=connector_id, car_connectivity=car_connectivity, config=config, log=LOG, api_log=LOG_API)
|
|
76
|
+
self._healthy = False
|
|
75
77
|
|
|
76
78
|
self._mqtt_client: SkodaMQTTClient = SkodaMQTTClient(skoda_connector=self)
|
|
77
79
|
|
|
@@ -81,15 +83,8 @@ class Connector(BaseConnector):
|
|
|
81
83
|
|
|
82
84
|
self.connected: BooleanAttribute = BooleanAttribute(name="connected", parent=self, tags={'connector_custom'})
|
|
83
85
|
self.interval: DurationAttribute = DurationAttribute(name="interval", parent=self, tags={'connector_custom'})
|
|
84
|
-
|
|
85
|
-
def __check_interval(attribute: GenericAttribute, value: Any) -> Any:
|
|
86
|
-
del attribute
|
|
87
|
-
if value is not None and value < timedelta(seconds=180):
|
|
88
|
-
raise ValueError('Intervall must be at least 180 seconds')
|
|
89
|
-
return value
|
|
90
|
-
|
|
86
|
+
self.interval.minimum = timedelta(seconds=180)
|
|
91
87
|
self.interval._is_changeable = True # pylint: disable=protected-access
|
|
92
|
-
self.interval._add_on_set_hook(__check_interval) # pylint: disable=protected-access
|
|
93
88
|
|
|
94
89
|
self.commands: Commands = Commands(parent=self)
|
|
95
90
|
|
|
@@ -172,6 +167,7 @@ class Connector(BaseConnector):
|
|
|
172
167
|
self._background_connect_thread.start()
|
|
173
168
|
# Start MQTT thread
|
|
174
169
|
self._mqtt_client.loop_start()
|
|
170
|
+
self._healthy = True
|
|
175
171
|
|
|
176
172
|
def _background_connect_loop(self) -> None:
|
|
177
173
|
while not self._stop_event.is_set():
|
|
@@ -216,6 +212,10 @@ class Connector(BaseConnector):
|
|
|
216
212
|
except TemporaryAuthenticationError as err:
|
|
217
213
|
LOG.error('Temporary authentification error during update (%s). Will try again after configured interval of %ss', str(err), interval)
|
|
218
214
|
self._stop_event.wait(interval)
|
|
215
|
+
except Exception as err:
|
|
216
|
+
LOG.critical('Critical error during update: %s', traceback.format_exc())
|
|
217
|
+
self._healthy = False
|
|
218
|
+
raise err
|
|
219
219
|
else:
|
|
220
220
|
self._stop_event.wait(interval)
|
|
221
221
|
|
|
@@ -362,6 +362,7 @@ class Connector(BaseConnector):
|
|
|
362
362
|
if vehicle_to_update.capabilities.has_capability('VEHICLE_HEALTH_INSPECTION'):
|
|
363
363
|
vehicle_to_update = self.fetch_maintenance(vehicle_to_update)
|
|
364
364
|
vehicle_to_update = self.decide_state(vehicle_to_update)
|
|
365
|
+
self.car_connectivity.transaction_end()
|
|
365
366
|
|
|
366
367
|
def decide_state(self, vehicle: SkodaVehicle) -> SkodaVehicle:
|
|
367
368
|
"""
|
|
@@ -786,12 +787,19 @@ class Connector(BaseConnector):
|
|
|
786
787
|
# pylint: disable-next=protected-access
|
|
787
788
|
vehicle.climatization.settings.target_temperature._add_on_set_hook(self.__on_air_conditioning_target_temperature_change)
|
|
788
789
|
vehicle.climatization.settings.target_temperature._is_changeable = True # pylint: disable=protected-access
|
|
790
|
+
precision: float = 0.5
|
|
791
|
+
min_temperature: Optional[float] = None
|
|
792
|
+
max_temperature: Optional[float] = None
|
|
789
793
|
unit: Temperature = Temperature.UNKNOWN
|
|
790
794
|
if 'unitInCar' in data['targetTemperature'] and data['targetTemperature']['unitInCar'] is not None:
|
|
791
795
|
if data['targetTemperature']['unitInCar'] == 'CELSIUS':
|
|
792
796
|
unit = Temperature.C
|
|
797
|
+
min_temperature: Optional[float] = 16
|
|
798
|
+
max_temperature: Optional[float] = 29.5
|
|
793
799
|
elif data['targetTemperature']['unitInCar'] == 'FAHRENHEIT':
|
|
794
800
|
unit = Temperature.F
|
|
801
|
+
min_temperature: Optional[float] = 61
|
|
802
|
+
max_temperature: Optional[float] = 85
|
|
795
803
|
elif data['targetTemperature']['unitInCar'] == 'KELVIN':
|
|
796
804
|
unit = Temperature.K
|
|
797
805
|
else:
|
|
@@ -801,6 +809,10 @@ class Connector(BaseConnector):
|
|
|
801
809
|
vehicle.climatization.settings.target_temperature._set_value(value=data['targetTemperature']['temperatureValue'],
|
|
802
810
|
measured=captured_at,
|
|
803
811
|
unit=unit)
|
|
812
|
+
vehicle.climatization.settings.target_temperature.precision = precision
|
|
813
|
+
vehicle.climatization.settings.target_temperature.minimum = min_temperature
|
|
814
|
+
vehicle.climatization.settings.target_temperature.maximum = max_temperature
|
|
815
|
+
|
|
804
816
|
else:
|
|
805
817
|
# pylint: disable-next=protected-access
|
|
806
818
|
vehicle.climatization.settings.target_temperature._set_value(value=None, measured=captured_at, unit=unit)
|
|
@@ -1498,9 +1510,10 @@ class Connector(BaseConnector):
|
|
|
1498
1510
|
if command_arguments['command'] == ClimatizationStartStopCommand.Command.START:
|
|
1499
1511
|
command_dict['heaterSource'] = 'ELECTRIC'
|
|
1500
1512
|
command_dict['targetTemperature'] = {}
|
|
1513
|
+
precision: float = 0.5
|
|
1501
1514
|
if 'target_temperature' in command_arguments:
|
|
1502
1515
|
# Round target temperature to nearest 0.5
|
|
1503
|
-
command_dict['targetTemperature']['temperatureValue'] = round(command_arguments['target_temperature']
|
|
1516
|
+
command_dict['targetTemperature']['temperatureValue'] = round(command_arguments['target_temperature'] / precision) * precision
|
|
1504
1517
|
if 'target_temperature_unit' in command_arguments:
|
|
1505
1518
|
if not isinstance(command_arguments['target_temperature_unit'], Temperature):
|
|
1506
1519
|
raise CommandError('Temperature unit is not of type Temperature')
|
|
@@ -1518,8 +1531,10 @@ class Connector(BaseConnector):
|
|
|
1518
1531
|
and isinstance(climatization, Climatization) and climatization.settings is not None \
|
|
1519
1532
|
and climatization.settings.target_temperature is not None and climatization.settings.target_temperature.enabled \
|
|
1520
1533
|
and climatization.settings.target_temperature.value is not None: # pylint: disable=too-many-boolean-expressions
|
|
1534
|
+
if climatization.settings.target_temperature.precision is not None:
|
|
1535
|
+
precision = climatization.settings.target_temperature.precision
|
|
1521
1536
|
# Round target temperature to nearest 0.5
|
|
1522
|
-
command_dict['targetTemperature']['temperatureValue'] = round(climatization.settings.target_temperature.value
|
|
1537
|
+
command_dict['targetTemperature']['temperatureValue'] = round(climatization.settings.target_temperature.value / precision) * precision
|
|
1523
1538
|
if climatization.settings.target_temperature.unit == Temperature.C:
|
|
1524
1539
|
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1525
1540
|
elif climatization.settings.target_temperature.unit == Temperature.F:
|
|
@@ -1751,3 +1766,6 @@ class Connector(BaseConnector):
|
|
|
1751
1766
|
except requests.exceptions.RetryError as retry_error:
|
|
1752
1767
|
raise CommandError(f'Retrying failed: {retry_error}') from retry_error
|
|
1753
1768
|
return command_arguments
|
|
1769
|
+
|
|
1770
|
+
def is_healthy(self) -> bool:
|
|
1771
|
+
return self._healthy and super().is_healthy()
|
|
@@ -469,7 +469,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
469
469
|
if 'data' in data and data['data'] is not None:
|
|
470
470
|
vehicle: Optional[GenericVehicle] = self._skoda_connector.car_connectivity.garage.get_vehicle(vin)
|
|
471
471
|
if isinstance(vehicle, SkodaElectricVehicle):
|
|
472
|
-
electric_drive: ElectricDrive = vehicle.get_electric_drive()
|
|
472
|
+
electric_drive: Optional[ElectricDrive] = vehicle.get_electric_drive()
|
|
473
473
|
if electric_drive is not None:
|
|
474
474
|
charging_state: Optional[Charging.ChargingState] = vehicle.charging.state.value
|
|
475
475
|
old_charging_state: Optional[Charging.ChargingState] = charging_state
|
|
@@ -509,6 +509,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
509
509
|
if old_charging_state != charging_state:
|
|
510
510
|
try:
|
|
511
511
|
self._skoda_connector.fetch_charging(vehicle, no_cache=True)
|
|
512
|
+
self._skoda_connector.car_connectivity.transaction_end()
|
|
512
513
|
except CarConnectivityError as e:
|
|
513
514
|
LOG.error('Error while fetching charging: %s', e)
|
|
514
515
|
if 'timeToFinish' in data['data'] and data['data']['timeToFinish'] is not None \
|
|
@@ -536,6 +537,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
536
537
|
if isinstance(vehicle, SkodaVehicle):
|
|
537
538
|
try:
|
|
538
539
|
self._skoda_connector.fetch_air_conditioning(vehicle, no_cache=True)
|
|
540
|
+
self._skoda_connector.car_connectivity.transaction_end()
|
|
539
541
|
except CarConnectivityError as e:
|
|
540
542
|
LOG.error('Error while fetching air conditioning: %s', e)
|
|
541
543
|
elif 'name' in data and data['name'] == 'climatisation-completed':
|
|
@@ -582,6 +584,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
582
584
|
self._skoda_connector.fetch_air_conditioning(vehicle, no_cache=True)
|
|
583
585
|
except CarConnectivityError as e:
|
|
584
586
|
LOG.error('Error while fetching air conditioning: %s', e)
|
|
587
|
+
self._skoda_connector.car_connectivity.transaction_end()
|
|
585
588
|
|
|
586
589
|
if vin in self.delayed_access_function_timers:
|
|
587
590
|
self.delayed_access_function_timers[vin].cancel()
|
|
@@ -598,6 +601,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
598
601
|
if isinstance(vehicle, SkodaVehicle):
|
|
599
602
|
try:
|
|
600
603
|
self._skoda_connector.fetch_vehicle_status(vehicle, no_cache=True)
|
|
604
|
+
self._skoda_connector.car_connectivity.transaction_end()
|
|
601
605
|
except CarConnectivityError as e:
|
|
602
606
|
LOG.error('Error while fetching vehicle status: %s', e)
|
|
603
607
|
|
|
@@ -629,6 +633,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
629
633
|
LOG.debug('Received %s operation request for vehicle %s from user %s', operation_request, vin, user_id)
|
|
630
634
|
try:
|
|
631
635
|
self._skoda_connector.fetch_air_conditioning(vehicle, no_cache=True)
|
|
636
|
+
self._skoda_connector.car_connectivity.transaction_end()
|
|
632
637
|
except CarConnectivityError as e:
|
|
633
638
|
LOG.error('Error while fetching air-conditioning: %s', e)
|
|
634
639
|
return
|
|
@@ -649,6 +654,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
649
654
|
LOG.debug('Received %s operation request for vehicle %s from user %s', operation_request, vin, user_id)
|
|
650
655
|
try:
|
|
651
656
|
self._skoda_connector.fetch_charging(vehicle, no_cache=True)
|
|
657
|
+
self._skoda_connector.car_connectivity.transaction_end()
|
|
652
658
|
except CarConnectivityError as e:
|
|
653
659
|
LOG.error('Error while fetching charging: %s', e)
|
|
654
660
|
return
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/CHANGELOG.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{carconnectivity_connector_skoda-0.4a4 → carconnectivity_connector_skoda-0.4a6}/doc/Config.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|