indevolt-api 1.2.3__tar.gz → 1.3.0__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.
- {indevolt_api-1.2.3 → indevolt_api-1.3.0}/PKG-INFO +78 -2
- {indevolt_api-1.2.3 → indevolt_api-1.3.0}/indevolt_api/__init__.py +12 -2
- {indevolt_api-1.2.3 → indevolt_api-1.3.0}/indevolt_api/client.py +72 -0
- {indevolt_api-1.2.3 → indevolt_api-1.3.0}/indevolt_api.egg-info/PKG-INFO +78 -2
- {indevolt_api-1.2.3 → indevolt_api-1.3.0}/pyproject.toml +1 -1
- {indevolt_api-1.2.3 → indevolt_api-1.3.0}/readme.md +77 -1
- {indevolt_api-1.2.3 → indevolt_api-1.3.0}/LICENSE +0 -0
- {indevolt_api-1.2.3 → indevolt_api-1.3.0}/indevolt_api.egg-info/SOURCES.txt +0 -0
- {indevolt_api-1.2.3 → indevolt_api-1.3.0}/indevolt_api.egg-info/dependency_links.txt +0 -0
- {indevolt_api-1.2.3 → indevolt_api-1.3.0}/indevolt_api.egg-info/requires.txt +0 -0
- {indevolt_api-1.2.3 → indevolt_api-1.3.0}/indevolt_api.egg-info/top_level.txt +0 -0
- {indevolt_api-1.2.3 → indevolt_api-1.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: indevolt-api
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Python API client for Indevolt devices
|
|
5
5
|
Author: A. Gideonse
|
|
6
6
|
License: MIT
|
|
@@ -223,6 +223,64 @@ config = await api.get_config()
|
|
|
223
223
|
print(config)
|
|
224
224
|
```
|
|
225
225
|
|
|
226
|
+
#### `check_charge_limits(power: int, target_soc: int, generation: int) -> None`
|
|
227
|
+
|
|
228
|
+
Check that charge parameters do not exceed device limits. Raises an exception if any boundary is violated.
|
|
229
|
+
|
|
230
|
+
**Parameters:**
|
|
231
|
+
|
|
232
|
+
- `power` (int): Requested charge power in watts
|
|
233
|
+
- `target_soc` (int): Target state of charge percentage
|
|
234
|
+
- `generation` (int): Device hardware generation (`1` or `2`), available from `get_config()` under `device.generation`
|
|
235
|
+
|
|
236
|
+
**Raises:**
|
|
237
|
+
|
|
238
|
+
- `ChargePowerExceedsMaxError`: If `power` exceeds the maximum for the given generation
|
|
239
|
+
- `SocBelowMinimumError`: If `target_soc` is below `MINIMUM_SOC` (5%)
|
|
240
|
+
|
|
241
|
+
**Example:**
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
config = await api.get_config()
|
|
245
|
+
generation = config["device"]["generation"]
|
|
246
|
+
|
|
247
|
+
try:
|
|
248
|
+
api.check_charge_limits(power=1000, target_soc=80, generation=generation)
|
|
249
|
+
except ChargePowerExceedsMaxError as e:
|
|
250
|
+
print(f"Power {e.power}W exceeds max {e.max_power}W for gen {e.generation}")
|
|
251
|
+
except SocBelowMinimumError as e:
|
|
252
|
+
print(f"Target SOC {e.target_soc}% is below minimum {e.minimum_soc}%")
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### `check_discharge_limits(power: int, target_soc: int, generation: int) -> None`
|
|
256
|
+
|
|
257
|
+
Check that discharge parameters do not exceed device limits. Raises an exception if any boundary is violated.
|
|
258
|
+
|
|
259
|
+
**Parameters:**
|
|
260
|
+
|
|
261
|
+
- `power` (int): Requested discharge power in watts
|
|
262
|
+
- `target_soc` (int): Target state of charge percentage
|
|
263
|
+
- `generation` (int): Device hardware generation (`1` or `2`), available from `get_config()` under `device.generation`
|
|
264
|
+
|
|
265
|
+
**Raises:**
|
|
266
|
+
|
|
267
|
+
- `DischargePowerExceedsMaxError`: If `power` exceeds the maximum for the given generation
|
|
268
|
+
- `SocBelowMinimumError`: If `target_soc` is below `MINIMUM_SOC` (5%)
|
|
269
|
+
|
|
270
|
+
**Example:**
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
config = await api.get_config()
|
|
274
|
+
generation = config["device"]["generation"]
|
|
275
|
+
|
|
276
|
+
try:
|
|
277
|
+
api.check_discharge_limits(power=600, target_soc=10, generation=generation)
|
|
278
|
+
except DischargePowerExceedsMaxError as e:
|
|
279
|
+
print(f"Power {e.power}W exceeds max {e.max_power}W for gen {e.generation}")
|
|
280
|
+
except SocBelowMinimumError as e:
|
|
281
|
+
print(f"Target SOC {e.target_soc}% is below minimum {e.minimum_soc}%")
|
|
282
|
+
```
|
|
283
|
+
|
|
226
284
|
### async_discover(timeout: float = 5.0) -> list[DiscoveredDevice]
|
|
227
285
|
|
|
228
286
|
Discover Indevolt devices on the local network using UDP broadcast.
|
|
@@ -265,7 +323,7 @@ if device.name:
|
|
|
265
323
|
|
|
266
324
|
## Exception Handling
|
|
267
325
|
|
|
268
|
-
The library provides
|
|
326
|
+
The library provides custom exceptions for API errors and limit violations.
|
|
269
327
|
|
|
270
328
|
### `APIException`
|
|
271
329
|
|
|
@@ -275,6 +333,24 @@ Raised when there's a client error during API communication (network errors, HTT
|
|
|
275
333
|
|
|
276
334
|
Raised when an API request times out (default timeout: 10 seconds).
|
|
277
335
|
|
|
336
|
+
### `ChargePowerExceedsMaxError`
|
|
337
|
+
|
|
338
|
+
Raised by `check_charge_limits()` when the requested charge power exceeds the device maximum.
|
|
339
|
+
|
|
340
|
+
**Attributes:** `power`, `max_power`, `generation`
|
|
341
|
+
|
|
342
|
+
### `DischargePowerExceedsMaxError`
|
|
343
|
+
|
|
344
|
+
Raised by `check_discharge_limits()` when the requested discharge power exceeds the device maximum.
|
|
345
|
+
|
|
346
|
+
**Attributes:** `power`, `max_power`, `generation`
|
|
347
|
+
|
|
348
|
+
### `SocBelowMinimumError`
|
|
349
|
+
|
|
350
|
+
Raised by `check_charge_limits()` or `check_discharge_limits()` when the target SOC is below the hard minimum of 5%.
|
|
351
|
+
|
|
352
|
+
**Attributes:** `target_soc`, `minimum_soc`
|
|
353
|
+
|
|
278
354
|
**Example:**
|
|
279
355
|
|
|
280
356
|
```python
|
|
@@ -2,18 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
from .client import (
|
|
4
4
|
APIException,
|
|
5
|
+
ChargePowerExceedsMaxError,
|
|
5
6
|
DiscoveredDevice,
|
|
7
|
+
DischargePowerExceedsMaxError,
|
|
6
8
|
IndevoltAPI,
|
|
9
|
+
MINIMUM_SOC,
|
|
10
|
+
POWER_LIMITS,
|
|
11
|
+
SocBelowMinimumError,
|
|
7
12
|
TimeOutException,
|
|
8
13
|
async_discover,
|
|
9
14
|
)
|
|
10
15
|
|
|
11
|
-
__version__ = "1.
|
|
16
|
+
__version__ = "1.3.0"
|
|
12
17
|
|
|
13
18
|
__all__ = [
|
|
14
19
|
"IndevoltAPI",
|
|
15
20
|
"APIException",
|
|
21
|
+
"ChargePowerExceedsMaxError",
|
|
22
|
+
"DiscoveredDevice",
|
|
23
|
+
"DischargePowerExceedsMaxError",
|
|
24
|
+
"MINIMUM_SOC",
|
|
25
|
+
"POWER_LIMITS",
|
|
26
|
+
"SocBelowMinimumError",
|
|
16
27
|
"TimeOutException",
|
|
17
28
|
"async_discover",
|
|
18
|
-
"DiscoveredDevice",
|
|
19
29
|
]
|
|
@@ -18,6 +18,40 @@ class APIException(Exception):
|
|
|
18
18
|
"""Raised on client error during API call."""
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
MINIMUM_SOC: int = 5
|
|
22
|
+
|
|
23
|
+
POWER_LIMITS: dict[int, dict[str, int]] = {
|
|
24
|
+
1: {"max_discharge_power": 800, "max_charge_power": 1200},
|
|
25
|
+
2: {"max_discharge_power": 2400, "max_charge_power": 2400},
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ChargePowerExceedsMaxError(Exception):
|
|
30
|
+
"""Raised when requested charge power exceeds the device maximum."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, power: int, max_power: int, generation: int) -> None:
|
|
33
|
+
self.power = power
|
|
34
|
+
self.max_power = max_power
|
|
35
|
+
self.generation = generation
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class DischargePowerExceedsMaxError(Exception):
|
|
39
|
+
"""Raised when requested discharge power exceeds the device maximum."""
|
|
40
|
+
|
|
41
|
+
def __init__(self, power: int, max_power: int, generation: int) -> None:
|
|
42
|
+
self.power = power
|
|
43
|
+
self.max_power = max_power
|
|
44
|
+
self.generation = generation
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class SocBelowMinimumError(Exception):
|
|
48
|
+
"""Raised when target SOC is below the API's hard minimum."""
|
|
49
|
+
|
|
50
|
+
def __init__(self, target_soc: int) -> None:
|
|
51
|
+
self.target_soc = target_soc
|
|
52
|
+
self.minimum_soc = MINIMUM_SOC
|
|
53
|
+
|
|
54
|
+
|
|
21
55
|
# Discovery configuration
|
|
22
56
|
DISCOVERY_PORT = 10000
|
|
23
57
|
BROADCAST_PORT = 8099
|
|
@@ -282,6 +316,44 @@ class IndevoltAPI:
|
|
|
282
316
|
|
|
283
317
|
return bool(response.get("result", False))
|
|
284
318
|
|
|
319
|
+
def check_charge_limits(self, power: int, target_soc: int, generation: int) -> None:
|
|
320
|
+
"""Check that charge parameters do not exceed device limits.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
power: Requested charge power in watts
|
|
324
|
+
target_soc: Target state of charge percentage
|
|
325
|
+
generation: Device hardware generation (1 or 2)
|
|
326
|
+
|
|
327
|
+
Raises:
|
|
328
|
+
ChargePowerExceedsMaxError: If power exceeds the device maximum
|
|
329
|
+
SocBelowMinimumError: If target_soc is below MINIMUM_SOC
|
|
330
|
+
"""
|
|
331
|
+
max_power = POWER_LIMITS[generation]["max_charge_power"]
|
|
332
|
+
if power > max_power:
|
|
333
|
+
raise ChargePowerExceedsMaxError(power, max_power, generation)
|
|
334
|
+
if target_soc < MINIMUM_SOC:
|
|
335
|
+
raise SocBelowMinimumError(target_soc)
|
|
336
|
+
|
|
337
|
+
def check_discharge_limits(
|
|
338
|
+
self, power: int, target_soc: int, generation: int
|
|
339
|
+
) -> None:
|
|
340
|
+
"""Check that discharge parameters do not exceed device limits.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
power: Requested discharge power in watts
|
|
344
|
+
target_soc: Target state of charge percentage
|
|
345
|
+
generation: Device hardware generation (1 or 2)
|
|
346
|
+
|
|
347
|
+
Raises:
|
|
348
|
+
DischargePowerExceedsMaxError: If power exceeds the device maximum
|
|
349
|
+
SocBelowMinimumError: If target_soc is below MINIMUM_SOC
|
|
350
|
+
"""
|
|
351
|
+
max_power = POWER_LIMITS[generation]["max_discharge_power"]
|
|
352
|
+
if power > max_power:
|
|
353
|
+
raise DischargePowerExceedsMaxError(power, max_power, generation)
|
|
354
|
+
if target_soc < MINIMUM_SOC:
|
|
355
|
+
raise SocBelowMinimumError(target_soc)
|
|
356
|
+
|
|
285
357
|
async def get_config(self) -> dict[str, Any]:
|
|
286
358
|
"""Get system configuration from the device.
|
|
287
359
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: indevolt-api
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Python API client for Indevolt devices
|
|
5
5
|
Author: A. Gideonse
|
|
6
6
|
License: MIT
|
|
@@ -223,6 +223,64 @@ config = await api.get_config()
|
|
|
223
223
|
print(config)
|
|
224
224
|
```
|
|
225
225
|
|
|
226
|
+
#### `check_charge_limits(power: int, target_soc: int, generation: int) -> None`
|
|
227
|
+
|
|
228
|
+
Check that charge parameters do not exceed device limits. Raises an exception if any boundary is violated.
|
|
229
|
+
|
|
230
|
+
**Parameters:**
|
|
231
|
+
|
|
232
|
+
- `power` (int): Requested charge power in watts
|
|
233
|
+
- `target_soc` (int): Target state of charge percentage
|
|
234
|
+
- `generation` (int): Device hardware generation (`1` or `2`), available from `get_config()` under `device.generation`
|
|
235
|
+
|
|
236
|
+
**Raises:**
|
|
237
|
+
|
|
238
|
+
- `ChargePowerExceedsMaxError`: If `power` exceeds the maximum for the given generation
|
|
239
|
+
- `SocBelowMinimumError`: If `target_soc` is below `MINIMUM_SOC` (5%)
|
|
240
|
+
|
|
241
|
+
**Example:**
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
config = await api.get_config()
|
|
245
|
+
generation = config["device"]["generation"]
|
|
246
|
+
|
|
247
|
+
try:
|
|
248
|
+
api.check_charge_limits(power=1000, target_soc=80, generation=generation)
|
|
249
|
+
except ChargePowerExceedsMaxError as e:
|
|
250
|
+
print(f"Power {e.power}W exceeds max {e.max_power}W for gen {e.generation}")
|
|
251
|
+
except SocBelowMinimumError as e:
|
|
252
|
+
print(f"Target SOC {e.target_soc}% is below minimum {e.minimum_soc}%")
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### `check_discharge_limits(power: int, target_soc: int, generation: int) -> None`
|
|
256
|
+
|
|
257
|
+
Check that discharge parameters do not exceed device limits. Raises an exception if any boundary is violated.
|
|
258
|
+
|
|
259
|
+
**Parameters:**
|
|
260
|
+
|
|
261
|
+
- `power` (int): Requested discharge power in watts
|
|
262
|
+
- `target_soc` (int): Target state of charge percentage
|
|
263
|
+
- `generation` (int): Device hardware generation (`1` or `2`), available from `get_config()` under `device.generation`
|
|
264
|
+
|
|
265
|
+
**Raises:**
|
|
266
|
+
|
|
267
|
+
- `DischargePowerExceedsMaxError`: If `power` exceeds the maximum for the given generation
|
|
268
|
+
- `SocBelowMinimumError`: If `target_soc` is below `MINIMUM_SOC` (5%)
|
|
269
|
+
|
|
270
|
+
**Example:**
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
config = await api.get_config()
|
|
274
|
+
generation = config["device"]["generation"]
|
|
275
|
+
|
|
276
|
+
try:
|
|
277
|
+
api.check_discharge_limits(power=600, target_soc=10, generation=generation)
|
|
278
|
+
except DischargePowerExceedsMaxError as e:
|
|
279
|
+
print(f"Power {e.power}W exceeds max {e.max_power}W for gen {e.generation}")
|
|
280
|
+
except SocBelowMinimumError as e:
|
|
281
|
+
print(f"Target SOC {e.target_soc}% is below minimum {e.minimum_soc}%")
|
|
282
|
+
```
|
|
283
|
+
|
|
226
284
|
### async_discover(timeout: float = 5.0) -> list[DiscoveredDevice]
|
|
227
285
|
|
|
228
286
|
Discover Indevolt devices on the local network using UDP broadcast.
|
|
@@ -265,7 +323,7 @@ if device.name:
|
|
|
265
323
|
|
|
266
324
|
## Exception Handling
|
|
267
325
|
|
|
268
|
-
The library provides
|
|
326
|
+
The library provides custom exceptions for API errors and limit violations.
|
|
269
327
|
|
|
270
328
|
### `APIException`
|
|
271
329
|
|
|
@@ -275,6 +333,24 @@ Raised when there's a client error during API communication (network errors, HTT
|
|
|
275
333
|
|
|
276
334
|
Raised when an API request times out (default timeout: 10 seconds).
|
|
277
335
|
|
|
336
|
+
### `ChargePowerExceedsMaxError`
|
|
337
|
+
|
|
338
|
+
Raised by `check_charge_limits()` when the requested charge power exceeds the device maximum.
|
|
339
|
+
|
|
340
|
+
**Attributes:** `power`, `max_power`, `generation`
|
|
341
|
+
|
|
342
|
+
### `DischargePowerExceedsMaxError`
|
|
343
|
+
|
|
344
|
+
Raised by `check_discharge_limits()` when the requested discharge power exceeds the device maximum.
|
|
345
|
+
|
|
346
|
+
**Attributes:** `power`, `max_power`, `generation`
|
|
347
|
+
|
|
348
|
+
### `SocBelowMinimumError`
|
|
349
|
+
|
|
350
|
+
Raised by `check_charge_limits()` or `check_discharge_limits()` when the target SOC is below the hard minimum of 5%.
|
|
351
|
+
|
|
352
|
+
**Attributes:** `target_soc`, `minimum_soc`
|
|
353
|
+
|
|
278
354
|
**Example:**
|
|
279
355
|
|
|
280
356
|
```python
|
|
@@ -197,6 +197,64 @@ config = await api.get_config()
|
|
|
197
197
|
print(config)
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
+
#### `check_charge_limits(power: int, target_soc: int, generation: int) -> None`
|
|
201
|
+
|
|
202
|
+
Check that charge parameters do not exceed device limits. Raises an exception if any boundary is violated.
|
|
203
|
+
|
|
204
|
+
**Parameters:**
|
|
205
|
+
|
|
206
|
+
- `power` (int): Requested charge power in watts
|
|
207
|
+
- `target_soc` (int): Target state of charge percentage
|
|
208
|
+
- `generation` (int): Device hardware generation (`1` or `2`), available from `get_config()` under `device.generation`
|
|
209
|
+
|
|
210
|
+
**Raises:**
|
|
211
|
+
|
|
212
|
+
- `ChargePowerExceedsMaxError`: If `power` exceeds the maximum for the given generation
|
|
213
|
+
- `SocBelowMinimumError`: If `target_soc` is below `MINIMUM_SOC` (5%)
|
|
214
|
+
|
|
215
|
+
**Example:**
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
config = await api.get_config()
|
|
219
|
+
generation = config["device"]["generation"]
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
api.check_charge_limits(power=1000, target_soc=80, generation=generation)
|
|
223
|
+
except ChargePowerExceedsMaxError as e:
|
|
224
|
+
print(f"Power {e.power}W exceeds max {e.max_power}W for gen {e.generation}")
|
|
225
|
+
except SocBelowMinimumError as e:
|
|
226
|
+
print(f"Target SOC {e.target_soc}% is below minimum {e.minimum_soc}%")
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### `check_discharge_limits(power: int, target_soc: int, generation: int) -> None`
|
|
230
|
+
|
|
231
|
+
Check that discharge parameters do not exceed device limits. Raises an exception if any boundary is violated.
|
|
232
|
+
|
|
233
|
+
**Parameters:**
|
|
234
|
+
|
|
235
|
+
- `power` (int): Requested discharge power in watts
|
|
236
|
+
- `target_soc` (int): Target state of charge percentage
|
|
237
|
+
- `generation` (int): Device hardware generation (`1` or `2`), available from `get_config()` under `device.generation`
|
|
238
|
+
|
|
239
|
+
**Raises:**
|
|
240
|
+
|
|
241
|
+
- `DischargePowerExceedsMaxError`: If `power` exceeds the maximum for the given generation
|
|
242
|
+
- `SocBelowMinimumError`: If `target_soc` is below `MINIMUM_SOC` (5%)
|
|
243
|
+
|
|
244
|
+
**Example:**
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
config = await api.get_config()
|
|
248
|
+
generation = config["device"]["generation"]
|
|
249
|
+
|
|
250
|
+
try:
|
|
251
|
+
api.check_discharge_limits(power=600, target_soc=10, generation=generation)
|
|
252
|
+
except DischargePowerExceedsMaxError as e:
|
|
253
|
+
print(f"Power {e.power}W exceeds max {e.max_power}W for gen {e.generation}")
|
|
254
|
+
except SocBelowMinimumError as e:
|
|
255
|
+
print(f"Target SOC {e.target_soc}% is below minimum {e.minimum_soc}%")
|
|
256
|
+
```
|
|
257
|
+
|
|
200
258
|
### async_discover(timeout: float = 5.0) -> list[DiscoveredDevice]
|
|
201
259
|
|
|
202
260
|
Discover Indevolt devices on the local network using UDP broadcast.
|
|
@@ -239,7 +297,7 @@ if device.name:
|
|
|
239
297
|
|
|
240
298
|
## Exception Handling
|
|
241
299
|
|
|
242
|
-
The library provides
|
|
300
|
+
The library provides custom exceptions for API errors and limit violations.
|
|
243
301
|
|
|
244
302
|
### `APIException`
|
|
245
303
|
|
|
@@ -249,6 +307,24 @@ Raised when there's a client error during API communication (network errors, HTT
|
|
|
249
307
|
|
|
250
308
|
Raised when an API request times out (default timeout: 10 seconds).
|
|
251
309
|
|
|
310
|
+
### `ChargePowerExceedsMaxError`
|
|
311
|
+
|
|
312
|
+
Raised by `check_charge_limits()` when the requested charge power exceeds the device maximum.
|
|
313
|
+
|
|
314
|
+
**Attributes:** `power`, `max_power`, `generation`
|
|
315
|
+
|
|
316
|
+
### `DischargePowerExceedsMaxError`
|
|
317
|
+
|
|
318
|
+
Raised by `check_discharge_limits()` when the requested discharge power exceeds the device maximum.
|
|
319
|
+
|
|
320
|
+
**Attributes:** `power`, `max_power`, `generation`
|
|
321
|
+
|
|
322
|
+
### `SocBelowMinimumError`
|
|
323
|
+
|
|
324
|
+
Raised by `check_charge_limits()` or `check_discharge_limits()` when the target SOC is below the hard minimum of 5%.
|
|
325
|
+
|
|
326
|
+
**Attributes:** `target_soc`, `minimum_soc`
|
|
327
|
+
|
|
252
328
|
**Example:**
|
|
253
329
|
|
|
254
330
|
```python
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|