qolsys-controller 0.0.4__tar.gz → 0.0.7__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.
- qolsys_controller-0.0.4/.github/workflows/python-app.yml → qolsys_controller-0.0.7/.github/workflows/build.yml +1 -1
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/PKG-INFO +7 -3
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/README.md +6 -2
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/pyproject.toml +1 -1
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/panel.py +18 -16
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/plugin_remote.py +24 -2
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/settings.py +39 -1
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/.github/workflows/publish.yml +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/.gitignore +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/Info_mqtt.md +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/LICENSE +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/bin/qolsys.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/example.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/info_pairing.md +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/info_qolsys.md +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/__init__.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/controller.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/db.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_alarmedsensor.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_automation.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_country_locale.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_dashboard_msgs.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_dimmerlight.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_doorlock.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_eu_event.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_heat_map.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_history.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_iqremotesettings.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_iqrouter_network_config.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_iqrouter_user_device.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_master_slave.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_partition.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_powerg_device.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_qolsyssettings.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_scene.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_sensor.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_smartsocket.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_state.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_tcc.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_thermostat.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_trouble_conditions.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_user.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_virtual_device.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_weather.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_zigbee_device.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_zwave_association_group.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_zwave_history.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_zwave_node.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_zwave_other.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/enum.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/errors.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/mdns.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/mqtt_command_queue.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/observable.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/partition.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/pki.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/plugin.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/plugin_c4.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/state.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/task_manager.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/utils_mqtt.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/zone.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/zwave_device.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/zwave_dimmer.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/zwave_garagedoor.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/zwave_generic.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/zwave_lock.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/zwave_outlet.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/zwave_thermostat.py +0 -0
- {qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/requirements.txt +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
2
2
|
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
3
3
|
|
4
|
-
name:
|
4
|
+
name: Build
|
5
5
|
|
6
6
|
on:
|
7
7
|
push:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: qolsys-controller
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.7
|
4
4
|
Summary: A Python module that emulates a virtual IQ Remote device, enabling full local control of a Qolsys IQ Panel
|
5
5
|
Project-URL: Homepage, https://github.com/EHylands/QolsysController
|
6
6
|
Project-URL: Issues, https://github.com/EHylands/QolsysController/issues
|
@@ -14,7 +14,9 @@ Classifier: Topic :: Home Automation
|
|
14
14
|
Requires-Python: >=3.12
|
15
15
|
Description-Content-Type: text/markdown
|
16
16
|
|
17
|
-
#
|
17
|
+
# Qolsys Controller - qolsys-controller
|
18
|
+
|
19
|
+
[](https://github.com/EHylands/QolsysController/actions/workflows/build.yml)
|
18
20
|
|
19
21
|
A Python module that emulates a virtual IQ Remote device, enabling full **local control** of a Qolsys IQ Panel over MQTT — no cloud access required.
|
20
22
|
|
@@ -62,7 +64,9 @@ A Python module that emulates a virtual IQ Remote device, enabling full **local
|
|
62
64
|
|
63
65
|
## ⚠️ Certificate Warning
|
64
66
|
|
65
|
-
During pairing, the main panel issues **only one signed client certificate** per virtual IQ Remote. If any key files are lost or deleted, re-pairing may become impossible.
|
67
|
+
During pairing, the main panel issues **only one signed client certificate** per virtual IQ Remote. If any key files are lost or deleted, re-pairing may become impossible.
|
68
|
+
|
69
|
+
A new PKI, including a new private key, can be recreated under specific circumstances, though the precise conditions remain unknown at this time.
|
66
70
|
|
67
71
|
**Important:**
|
68
72
|
Immediately back up the following files from the `pki/` directory after initial pairing:
|
@@ -1,4 +1,6 @@
|
|
1
|
-
#
|
1
|
+
# Qolsys Controller - qolsys-controller
|
2
|
+
|
3
|
+
[](https://github.com/EHylands/QolsysController/actions/workflows/build.yml)
|
2
4
|
|
3
5
|
A Python module that emulates a virtual IQ Remote device, enabling full **local control** of a Qolsys IQ Panel over MQTT — no cloud access required.
|
4
6
|
|
@@ -46,7 +48,9 @@ A Python module that emulates a virtual IQ Remote device, enabling full **local
|
|
46
48
|
|
47
49
|
## ⚠️ Certificate Warning
|
48
50
|
|
49
|
-
During pairing, the main panel issues **only one signed client certificate** per virtual IQ Remote. If any key files are lost or deleted, re-pairing may become impossible.
|
51
|
+
During pairing, the main panel issues **only one signed client certificate** per virtual IQ Remote. If any key files are lost or deleted, re-pairing may become impossible.
|
52
|
+
|
53
|
+
A new PKI, including a new private key, can be recreated under specific circumstances, though the precise conditions remain unknown at this time.
|
50
54
|
|
51
55
|
**Important:**
|
52
56
|
Immediately back up the following files from the `pki/` directory after initial pairing:
|
@@ -93,22 +93,23 @@ class QolsysPanel(QolsysObservable):
|
|
93
93
|
self._product_type = ""
|
94
94
|
|
95
95
|
def read_users_file(self) -> bool:
|
96
|
-
# Loading user_code data from users.conf file
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
96
|
+
# Loading user_code data from users.conf file if exists
|
97
|
+
if self._settings.users_file_path.is_file():
|
98
|
+
try:
|
99
|
+
path = self._settings.users_file_path
|
100
|
+
with path.open("r", encoding="utf-8") as file:
|
101
|
+
try:
|
102
|
+
users = json.load(file)
|
103
|
+
for user in users:
|
104
|
+
self._users.append(user)
|
105
|
+
|
106
|
+
except json.JSONDecodeError:
|
107
|
+
LOGGER.exception("users.conf file json error")
|
108
|
+
return False
|
109
|
+
|
110
|
+
except FileNotFoundError:
|
111
|
+
LOGGER.exception("users.conf file not found")
|
112
|
+
return False
|
112
113
|
|
113
114
|
return True
|
114
115
|
|
@@ -817,6 +818,7 @@ class QolsysPanel(QolsysObservable):
|
|
817
818
|
LOGGER.debug("Android Version: %s", self.ANDROID_VERSION)
|
818
819
|
LOGGER.debug("Hardware Version: %s", self.HARDWARE_VERSION)
|
819
820
|
LOGGER.debug("MAC Address: %s", self.MAC_ADDRESS)
|
821
|
+
LOGGER.debug("MQTT remoteClientID: %s", self._settings.mqtt_remote_client_id)
|
820
822
|
LOGGER.debug("Unique ID: %s", self.unique_id)
|
821
823
|
LOGGER.debug("Panel Tamper State: %s", self.PANEL_TAMPER_STATE)
|
822
824
|
LOGGER.debug("AC Status: %s", self.AC_STATUS)
|
@@ -110,6 +110,9 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
110
110
|
else:
|
111
111
|
self._pki.set_id(self.settings.random_mac)
|
112
112
|
|
113
|
+
# Set mqtt_remote_client_id
|
114
|
+
self.settings.mqtt_remote_client_id = "qolsys-controller-" + self._pki.formatted_id()
|
115
|
+
|
113
116
|
# Check if plugin is paired
|
114
117
|
if self.is_paired():
|
115
118
|
LOGGER.debug("Panel is Paired")
|
@@ -133,6 +136,24 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
133
136
|
async def start_operation(self) -> None:
|
134
137
|
await self._task_manager.run(self.mqtt_connect_task(reconnect=True), self._mqtt_task_connect_label)
|
135
138
|
|
139
|
+
async def stop_operation(self) -> None:
|
140
|
+
LOGGER.debug("Stopping Plugin Operation")
|
141
|
+
|
142
|
+
if self.certificate_exchange_server is not None:
|
143
|
+
self.certificate_exchange_server.close()
|
144
|
+
|
145
|
+
if self.aiomqtt is not None:
|
146
|
+
await self.aiomqtt.__aexit__(None, None, None)
|
147
|
+
self.aiomqtt = None
|
148
|
+
|
149
|
+
self._task_manager.cancel(self._mqtt_task_connect_label)
|
150
|
+
self._task_manager.cancel(self._mqtt_task_listen_label)
|
151
|
+
self._task_manager.cancel(self._mqtt_task_ping_label)
|
152
|
+
self._task_manager.cancel(self._mqtt_task_config_label)
|
153
|
+
|
154
|
+
self.connected = False
|
155
|
+
self.connected_observer.notify()
|
156
|
+
|
136
157
|
async def mqtt_connect_task(self, reconnect: bool) -> None:
|
137
158
|
# Configure TLS parameters for MQTT connection
|
138
159
|
tls_params = aiomqtt.TLSParameters(
|
@@ -228,6 +249,7 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
228
249
|
while True:
|
229
250
|
if self.aiomqtt is not None and self.connected:
|
230
251
|
await self.command_pingevent()
|
252
|
+
|
231
253
|
await asyncio.sleep(self.settings.mqtt_ping)
|
232
254
|
|
233
255
|
async def mqtt_listen_task(self) -> None:
|
@@ -434,7 +456,7 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
434
456
|
ipAddress = self.settings.plugin_ip
|
435
457
|
eventName = "connect_v204"
|
436
458
|
macAddress = self.settings.random_mac
|
437
|
-
remoteClientID =
|
459
|
+
remoteClientID = self.settings.mqtt_remote_client_id
|
438
460
|
softwareVersion = "4.4.1"
|
439
461
|
producType = "tab07_rk68"
|
440
462
|
bssid = ""
|
@@ -646,7 +668,7 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
646
668
|
|
647
669
|
topic = "mastermeid"
|
648
670
|
eventName = "disconnect"
|
649
|
-
remoteClientID =
|
671
|
+
remoteClientID = self.settings.mqtt_remote_client_id
|
650
672
|
requestID = str(uuid.uuid4())
|
651
673
|
remoteMacAddress = self.settings.random_mac
|
652
674
|
|
@@ -24,7 +24,8 @@ class QolsysSettings:
|
|
24
24
|
|
25
25
|
# MQTT
|
26
26
|
self._mqtt_timeout: int = 30
|
27
|
-
self._mqtt_ping: int =
|
27
|
+
self._mqtt_ping: int = 15
|
28
|
+
self._mqtt_remote_client_id = ""
|
28
29
|
|
29
30
|
@property
|
30
31
|
def random_mac(self) -> str:
|
@@ -89,6 +90,14 @@ class QolsysSettings:
|
|
89
90
|
def mqtt_ping(self) -> int:
|
90
91
|
return self._mqtt_ping
|
91
92
|
|
93
|
+
@property
|
94
|
+
def mqtt_remote_client_id(self) -> str:
|
95
|
+
return self._mqtt_remote_client_id
|
96
|
+
|
97
|
+
@mqtt_remote_client_id.setter
|
98
|
+
def mqtt_remote_client_id(self,client_id:str) -> None:
|
99
|
+
self._mqtt_remote_client_id = client_id
|
100
|
+
|
92
101
|
def check_panel_ip(self) -> bool:
|
93
102
|
if self._panel_ip == "":
|
94
103
|
LOGGER.debug("Invalid Panel IP: %s", self._panel_ip)
|
@@ -123,4 +132,33 @@ class QolsysSettings:
|
|
123
132
|
return False
|
124
133
|
|
125
134
|
LOGGER.debug("Using config_directory: %s", self.config_directory.resolve())
|
135
|
+
|
136
|
+
# Create pki directory if not found
|
137
|
+
if not self.pki_directory.is_dir():
|
138
|
+
LOGGER.debug("Creating pki_directory: %s", self.pki_directory.resolve())
|
139
|
+
try:
|
140
|
+
self.pki_directory.mkdir(parents=True)
|
141
|
+
except PermissionError:
|
142
|
+
LOGGER.exception("Permission denied: Unable to create: %s", self.pki_directory.resolve())
|
143
|
+
return False
|
144
|
+
except Exception:
|
145
|
+
LOGGER.exception("Error creating pki_directory: %s", self.pki_directory.resolve())
|
146
|
+
return False
|
147
|
+
|
148
|
+
LOGGER.debug("Using pki_directory: %s", self.pki_directory.resolve())
|
149
|
+
|
150
|
+
# Create media directory if not found
|
151
|
+
if not self._media_directory.is_dir():
|
152
|
+
LOGGER.debug("Creating media_directory: %s", self._media_directory.resolve())
|
153
|
+
try:
|
154
|
+
self._media_directory.mkdir(parents=True)
|
155
|
+
except PermissionError:
|
156
|
+
LOGGER.exception("Permission denied: Unable to create: %s", self._media_directory.resolve())
|
157
|
+
return False
|
158
|
+
except Exception:
|
159
|
+
LOGGER.exception("Error creating media_directory: %s", self._media_directory.resolve())
|
160
|
+
return False
|
161
|
+
|
162
|
+
LOGGER.debug("Using media_directory: %s", self._media_directory.resolve())
|
163
|
+
|
126
164
|
return True
|
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
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_automation.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_dimmerlight.py
RENAMED
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_doorlock.py
RENAMED
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_eu_event.py
RENAMED
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_heat_map.py
RENAMED
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_history.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_master_slave.py
RENAMED
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_partition.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_scene.py
RENAMED
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_sensor.py
RENAMED
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_smartsocket.py
RENAMED
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_state.py
RENAMED
File without changes
|
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_thermostat.py
RENAMED
File without changes
|
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_user.py
RENAMED
File without changes
|
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_weather.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_zwave_node.py
RENAMED
File without changes
|
{qolsys_controller-0.0.4 → qolsys_controller-0.0.7}/qolsys_controller/database/table_zwave_other.py
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
|
File without changes
|
File without changes
|