carconnectivity-connector-skoda 0.1a19__tar.gz → 0.1a21__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.1a19 → carconnectivity_connector_skoda-0.1a21}/Makefile +1 -1
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/PKG-INFO +1 -1
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/doc/Config.md +2 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connector_skoda.egg-info/PKG-INFO +1 -1
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/_version.py +1 -1
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/charging.py +2 -2
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/connector.py +60 -8
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/mqtt_client.py +5 -2
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/vehicle.py +1 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/.flake8 +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/.github/dependabot.yml +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/.github/workflows/build.yml +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/.github/workflows/build_and_publish.yml +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/.github/workflows/codeql-analysis.yml +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/.gitignore +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/LICENSE +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/README.md +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/pyproject.toml +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/setup.cfg +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/setup_requirements.txt +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connector_skoda.egg-info/SOURCES.txt +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connector_skoda.egg-info/dependency_links.txt +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connector_skoda.egg-info/requires.txt +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connector_skoda.egg-info/top_level.txt +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/__init__.py +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/auth/__init__.py +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/auth/auth_util.py +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/auth/helpers/blacklist_retry.py +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/auth/my_skoda_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/auth/openid_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/auth/session_manager.py +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/auth/skoda_web_session.py +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/capability.py +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/climatization.py +0 -0
- {carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/src/carconnectivity_connectors/skoda/error.py +0 -0
|
@@ -6,7 +6,7 @@ test:
|
|
|
6
6
|
|
|
7
7
|
lint:
|
|
8
8
|
@echo "\n${BLUE}Running Pylint against source and test files...${NC}\n"
|
|
9
|
-
@pylint ./
|
|
9
|
+
@pylint ./src
|
|
10
10
|
@echo "\n${BLUE}Running Flake8 against source and test files...${NC}\n"
|
|
11
11
|
@flake8
|
|
12
12
|
@echo "\n${BLUE}Running Bandit against source files...${NC}\n"
|
{carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/doc/Config.md
RENAMED
|
@@ -10,11 +10,13 @@ These are the valid options for the MySkoda Connector
|
|
|
10
10
|
"connectors": [
|
|
11
11
|
{
|
|
12
12
|
"type": "skoda", // Definition for the MySkoda Connector
|
|
13
|
+
"disabled": false, // You can disable connectors without removing them from the config completely
|
|
13
14
|
"config": {
|
|
14
15
|
"log_level": "error", // set the connectos log level
|
|
15
16
|
"interval": 300, // Interval in which the server is checked in seconds
|
|
16
17
|
"username": "test@test.de", // Username of your Volkswagen Account
|
|
17
18
|
"password": "testpassword123", // Username of your Volkswagen Account
|
|
19
|
+
"spin": 1234, //S-Pin used for some special commands like locking/unlocking
|
|
18
20
|
"netrc": "~/.netr", // netrc file if to be used for passwords
|
|
19
21
|
"api_log_level": "debug", // Show debug information regarding the API
|
|
20
22
|
"max_age": 300 //Cache requests to the server vor MAX_AGE seconds
|
|
@@ -69,7 +69,7 @@ class SkodaCharging(Charging): # pylint: disable=too-many-instance-attributes
|
|
|
69
69
|
CONNECT_CABLE = 'connectCable'
|
|
70
70
|
READY_FOR_CHARGING = 'readyForCharging'
|
|
71
71
|
NOT_READY_FOR_CHARGING = 'notReadyForCharging'
|
|
72
|
-
|
|
72
|
+
CONSERVING = 'conserving'
|
|
73
73
|
CHARGE_PURPOSE_REACHED_NOT_CONSERVATION_CHARGING = 'chargePurposeReachedAndNotConservationCharging'
|
|
74
74
|
CHARGE_PURPOSE_REACHED_CONSERVATION = 'chargePurposeReachedAndConservation'
|
|
75
75
|
CHARGING = 'charging'
|
|
@@ -126,7 +126,7 @@ mapping_skoda_charging_state: Dict[SkodaCharging.SkodaChargingState, Charging.Ch
|
|
|
126
126
|
SkodaCharging.SkodaChargingState.CONNECT_CABLE: Charging.ChargingState.OFF,
|
|
127
127
|
SkodaCharging.SkodaChargingState.READY_FOR_CHARGING: Charging.ChargingState.READY_FOR_CHARGING,
|
|
128
128
|
SkodaCharging.SkodaChargingState.NOT_READY_FOR_CHARGING: Charging.ChargingState.OFF,
|
|
129
|
-
SkodaCharging.SkodaChargingState.
|
|
129
|
+
SkodaCharging.SkodaChargingState.CONSERVING: Charging.ChargingState.CONSERVATION,
|
|
130
130
|
SkodaCharging.SkodaChargingState.CHARGE_PURPOSE_REACHED_NOT_CONSERVATION_CHARGING: Charging.ChargingState.READY_FOR_CHARGING,
|
|
131
131
|
SkodaCharging.SkodaChargingState.CHARGE_PURPOSE_REACHED_CONSERVATION: Charging.ChargingState.CONSERVATION,
|
|
132
132
|
SkodaCharging.SkodaChargingState.CHARGING: Charging.ChargingState.CHARGING,
|
|
@@ -27,7 +27,7 @@ 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.command_impl import ClimatizationStartStopCommand
|
|
30
|
+
from carconnectivity.command_impl import ClimatizationStartStopCommand, ChargingStartStopCommand
|
|
31
31
|
|
|
32
32
|
from carconnectivity_connectors.base.connector import BaseConnector
|
|
33
33
|
from carconnectivity_connectors.skoda.auth.session_manager import SessionManager, SessionUser, Service
|
|
@@ -75,20 +75,20 @@ class Connector(BaseConnector):
|
|
|
75
75
|
# Configure logging
|
|
76
76
|
if 'log_level' in config and config['log_level'] is not None:
|
|
77
77
|
config['log_level'] = config['log_level'].upper()
|
|
78
|
-
if config['log_level'] in logging.
|
|
78
|
+
if config['log_level'] in logging._nameToLevel:
|
|
79
79
|
LOG.setLevel(config['log_level'])
|
|
80
80
|
self.log_level._set_value(config['log_level']) # pylint: disable=protected-access
|
|
81
81
|
logging.getLogger('requests').setLevel(config['log_level'])
|
|
82
82
|
logging.getLogger('urllib3').setLevel(config['log_level'])
|
|
83
83
|
logging.getLogger('oauthlib').setLevel(config['log_level'])
|
|
84
84
|
else:
|
|
85
|
-
raise ConfigurationError(f'Invalid log level: "{config["log_level"]}" not in {list(logging.
|
|
85
|
+
raise ConfigurationError(f'Invalid log level: "{config["log_level"]}" not in {list(logging._nameToLevel.keys())}')
|
|
86
86
|
if 'api_log_level' in config and config['api_log_level'] is not None:
|
|
87
87
|
config['api_log_level'] = config['api_log_level'].upper()
|
|
88
|
-
if config['api_log_level'] in logging.
|
|
88
|
+
if config['api_log_level'] in logging._nameToLevel:
|
|
89
89
|
LOG_API.setLevel(config['api_log_level'])
|
|
90
90
|
else:
|
|
91
|
-
raise ConfigurationError(f'Invalid log level: "{config["log_level"]}" not in {list(logging.
|
|
91
|
+
raise ConfigurationError(f'Invalid log level: "{config["log_level"]}" not in {list(logging._nameToLevel.keys())}')
|
|
92
92
|
LOG.info("Loading skoda connector with config %s", config_remove_credentials(self.config))
|
|
93
93
|
|
|
94
94
|
username: Optional[str] = None
|
|
@@ -287,7 +287,12 @@ class Connector(BaseConnector):
|
|
|
287
287
|
else:
|
|
288
288
|
vehicle.license_plate._set_value(None) # pylint: disable=protected-access
|
|
289
289
|
|
|
290
|
-
|
|
290
|
+
if 'name' in vehicle_dict and vehicle_dict['name'] is not None:
|
|
291
|
+
vehicle.name._set_value(vehicle_dict['name']) # pylint: disable=protected-access
|
|
292
|
+
else:
|
|
293
|
+
vehicle.name._set_value(None) # pylint: disable=protected-access
|
|
294
|
+
|
|
295
|
+
log_extra_keys(LOG_API, 'vehicles', vehicle_dict, {'vin', 'licensePlate', 'name'})
|
|
291
296
|
|
|
292
297
|
vehicle = self.fetch_vehicle_details(vehicle)
|
|
293
298
|
else:
|
|
@@ -343,6 +348,10 @@ class Connector(BaseConnector):
|
|
|
343
348
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}'
|
|
344
349
|
data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
|
345
350
|
if data is not None:
|
|
351
|
+
start_stop_command: ChargingStartStopCommand = ChargingStartStopCommand(parent=vehicle.charging.commands)
|
|
352
|
+
start_stop_command._add_on_set_hook(self.__on_charging_start_stop) # pylint: disable=protected-access
|
|
353
|
+
start_stop_command.enabled = True
|
|
354
|
+
vehicle.charging.commands.add_command(start_stop_command)
|
|
346
355
|
if 'carCapturedTimestamp' in data and data['carCapturedTimestamp'] is not None:
|
|
347
356
|
captured_at: datetime = robust_time_parse(data['carCapturedTimestamp'])
|
|
348
357
|
else:
|
|
@@ -1000,7 +1009,10 @@ class Connector(BaseConnector):
|
|
|
1000
1009
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/vehicle-status/{vin}'
|
|
1001
1010
|
vehicle_status_data: Dict[str, Any] | None = self._fetch_data(url=url, session=self.session, no_cache=no_cache)
|
|
1002
1011
|
if vehicle_status_data:
|
|
1003
|
-
|
|
1012
|
+
if 'carCapturedTimestamp' in vehicle_status_data and vehicle_status_data['carCapturedTimestamp'] is not None:
|
|
1013
|
+
captured_at: Optional[datetime] = robust_time_parse(vehicle_status_data['carCapturedTimestamp'])
|
|
1014
|
+
else:
|
|
1015
|
+
captured_at: Optional[datetime] = None
|
|
1004
1016
|
if 'overall' in vehicle_status_data and vehicle_status_data['overall'] is not None:
|
|
1005
1017
|
if 'doorsLocked' in vehicle_status_data['overall'] and vehicle_status_data['overall']['doorsLocked'] is not None \
|
|
1006
1018
|
and vehicle.doors is not None:
|
|
@@ -1421,11 +1433,24 @@ class Connector(BaseConnector):
|
|
|
1421
1433
|
raise SetterError(f'Unknown temperature unit {command_arguments['target_temperature_unit']}')
|
|
1422
1434
|
else:
|
|
1423
1435
|
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1436
|
+
elif start_stop_command.parent is not None and (climatization := start_stop_command.parent.parent) is not None \
|
|
1437
|
+
and isinstance(climatization, Climatization) and climatization.settings is not None \
|
|
1438
|
+
and climatization.settings.target_temperature is not None and climatization.settings.target_temperature.enabled \
|
|
1439
|
+
and climatization.settings.target_temperature.value is not None: # pylint: disable=too-many-boolean-expressions
|
|
1440
|
+
# Round target temperature to nearest 0.5
|
|
1441
|
+
command_dict['targetTemperature']['temperatureValue'] = round(climatization.settings.target_temperature.value * 2) / 2
|
|
1442
|
+
if climatization.settings.target_temperature.unit == Temperature.C:
|
|
1443
|
+
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1444
|
+
elif climatization.settings.target_temperature.unit == Temperature.F:
|
|
1445
|
+
command_dict['targetTemperature']['unitInCar'] = 'FAHRENHEIT'
|
|
1446
|
+
elif climatization.settings.target_temperature.unit == Temperature.K:
|
|
1447
|
+
command_dict['targetTemperature']['unitInCar'] = 'KELVIN'
|
|
1448
|
+
else:
|
|
1449
|
+
raise SetterError(f'Unknown temperature unit {climatization.settings.target_temperature.unit}')
|
|
1424
1450
|
else:
|
|
1425
1451
|
command_dict['targetTemperature']['temperatureValue'] = 25.0
|
|
1426
1452
|
command_dict['targetTemperature']['unitInCar'] = 'CELSIUS'
|
|
1427
1453
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/start'
|
|
1428
|
-
print(json.dumps(command_dict))
|
|
1429
1454
|
settings_response: requests.Response = self.session.post(url, data=json.dumps(command_dict), allow_redirects=True)
|
|
1430
1455
|
elif command_arguments['command'] == ClimatizationStartStopCommand.Command.STOP:
|
|
1431
1456
|
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v2/air-conditioning/{vin}/stop'
|
|
@@ -1437,3 +1462,30 @@ class Connector(BaseConnector):
|
|
|
1437
1462
|
LOG.error('Could not start/stop air conditioning (%s: %s)', settings_response.status_code, settings_response.text)
|
|
1438
1463
|
raise SetterError(f'Could not start/stop air conditioning ({settings_response.status_code}: {settings_response.text})')
|
|
1439
1464
|
return command_arguments
|
|
1465
|
+
|
|
1466
|
+
def __on_charging_start_stop(self, start_stop_command: ChargingStartStopCommand, command_arguments: Union[str, Dict[str, Any]]) \
|
|
1467
|
+
-> Union[str, Dict[str, Any]]:
|
|
1468
|
+
if start_stop_command.parent is None or start_stop_command.parent.parent is None \
|
|
1469
|
+
or start_stop_command.parent.parent.parent is None or not isinstance(start_stop_command.parent.parent.parent, SkodaVehicle):
|
|
1470
|
+
raise SetterError('Object hierarchy is not as expected')
|
|
1471
|
+
if not isinstance(command_arguments, dict):
|
|
1472
|
+
raise SetterError('Command arguments are not a dictionary')
|
|
1473
|
+
vehicle: SkodaVehicle = start_stop_command.parent.parent.parent
|
|
1474
|
+
vin: Optional[str] = vehicle.vin.value
|
|
1475
|
+
if vin is None:
|
|
1476
|
+
raise SetterError('VIN in object hierarchy missing')
|
|
1477
|
+
if 'command' not in command_arguments:
|
|
1478
|
+
raise SetterError('Command argument missing')
|
|
1479
|
+
if command_arguments['command'] == ChargingStartStopCommand.Command.START:
|
|
1480
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}/start'
|
|
1481
|
+
settings_response: requests.Response = self.session.post(url, allow_redirects=True)
|
|
1482
|
+
elif command_arguments['command'] == ChargingStartStopCommand.Command.STOP:
|
|
1483
|
+
url = f'https://mysmob.api.connect.skoda-auto.cz/api/v1/charging/{vin}/stop'
|
|
1484
|
+
settings_response: requests.Response = self.session.post(url, allow_redirects=True)
|
|
1485
|
+
else:
|
|
1486
|
+
raise SetterError(f'Unknown command {command_arguments["command"]}')
|
|
1487
|
+
|
|
1488
|
+
if settings_response.status_code != requests.codes['accepted']:
|
|
1489
|
+
LOG.error('Could not start/stop charging (%s: %s)', settings_response.status_code, settings_response.text)
|
|
1490
|
+
raise SetterError(f'Could not start/stop charging ({settings_response.status_code}: {settings_response.text})')
|
|
1491
|
+
return command_arguments
|
|
@@ -530,7 +530,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
530
530
|
try:
|
|
531
531
|
self._skoda_connector.fetch_air_conditioning(vehicle, no_cache=True)
|
|
532
532
|
except CarConnectivityError as e:
|
|
533
|
-
LOG.error('Error while fetching
|
|
533
|
+
LOG.error('Error while fetching air conditioning: %s', e)
|
|
534
534
|
elif 'name' in data and data['name'] == 'climatisation-completed':
|
|
535
535
|
if 'data' in data and data['data'] is not None:
|
|
536
536
|
vehicle: Optional[GenericVehicle] = self._skoda_connector.car_connectivity.garage.get_vehicle(vin)
|
|
@@ -628,7 +628,7 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
628
628
|
elif data['status'] == 'IN_PROGRESS':
|
|
629
629
|
LOG.debug('Received %s operation request for vehicle %s from user %s', operation_request, vin, user_id)
|
|
630
630
|
return
|
|
631
|
-
|
|
631
|
+
elif operation_request == 'charging/start-stop-charging' \
|
|
632
632
|
or operation_request == 'charging/update-battery-support' \
|
|
633
633
|
or operation_request == 'charging/update-auto-unlock-plug' \
|
|
634
634
|
or operation_request == 'charging/update-care-mode' \
|
|
@@ -645,6 +645,9 @@ class SkodaMQTTClient(Client): # pylint: disable=too-many-instance-attributes
|
|
|
645
645
|
except CarConnectivityError as e:
|
|
646
646
|
LOG.error('Error while fetching charging: %s', e)
|
|
647
647
|
return
|
|
648
|
+
elif data['status'] == 'IN_PROGRESS':
|
|
649
|
+
LOG.debug('Received %s operation request for vehicle %s from user %s', operation_request, vin, user_id)
|
|
650
|
+
return
|
|
648
651
|
LOG_API.info('Received unknown operation request %s for vehicle %s from user %s: %s', operation_request, vin, user_id, msg.payload)
|
|
649
652
|
return
|
|
650
653
|
LOG_API.info('I don\'t understand message %s: %s', msg.topic, msg.payload)
|
|
@@ -27,6 +27,7 @@ class SkodaVehicle(GenericVehicle): # pylint: disable=too-many-instance-attribu
|
|
|
27
27
|
else:
|
|
28
28
|
super().__init__(vin=vin, garage=garage, managing_connector=managing_connector)
|
|
29
29
|
self.capabilities = Capabilities(vehicle=self)
|
|
30
|
+
self.manufacturer._set_value(value='Škoda') # pylint: disable=protected-access
|
|
30
31
|
|
|
31
32
|
def __str__(self) -> str:
|
|
32
33
|
return_string: str = f'\t{self.capabilities}\n'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/.gitignore
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{carconnectivity_connector_skoda-0.1a19 → carconnectivity_connector_skoda-0.1a21}/pyproject.toml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|