violet-poolController-api 0.0.26__tar.gz → 0.0.29__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.
Files changed (26) hide show
  1. {violet_poolcontroller_api-0.0.26/violet_poolController_api.egg-info → violet_poolcontroller_api-0.0.29}/PKG-INFO +4 -9
  2. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/pyproject.toml +6 -6
  3. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/tests/test_api.py +153 -2
  4. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/tests/test_mock_server.py +49 -6
  5. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29/violet_poolController_api.egg-info}/PKG-INFO +4 -9
  6. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/violet_poolController_api.egg-info/SOURCES.txt +2 -4
  7. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/violet_poolController_api.egg-info/top_level.txt +0 -1
  8. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/violet_poolcontroller_api/__init__.py +135 -72
  9. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/violet_poolcontroller_api/api.py +326 -56
  10. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/violet_poolcontroller_api/circuit_breaker.py +205 -196
  11. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/violet_poolcontroller_api/const_api.py +594 -594
  12. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/violet_poolcontroller_api/const_devices.py +521 -351
  13. violet_poolcontroller_api-0.0.29/violet_poolcontroller_api/parsers.py +185 -0
  14. violet_poolcontroller_api-0.0.29/violet_poolcontroller_api/readings.py +515 -0
  15. violet_poolcontroller_api-0.0.26/setup.py +0 -36
  16. violet_poolcontroller_api-0.0.26/tests/__init__.py +0 -1
  17. violet_poolcontroller_api-0.0.26/tests/conftest.py +0 -40
  18. violet_poolcontroller_api-0.0.26/tests/mock_server.py +0 -684
  19. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/LICENSE +0 -0
  20. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/README.md +0 -0
  21. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/setup.cfg +0 -0
  22. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/tests/test_api_smoke.py +0 -0
  23. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/violet_poolController_api.egg-info/dependency_links.txt +0 -0
  24. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/violet_poolController_api.egg-info/requires.txt +0 -0
  25. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/violet_poolcontroller_api/utils_rate_limiter.py +0 -0
  26. {violet_poolcontroller_api-0.0.26 → violet_poolcontroller_api-0.0.29}/violet_poolcontroller_api/utils_sanitizer.py +0 -0
@@ -1,15 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: violet-poolController-api
3
- Version: 0.0.26
3
+ Version: 0.0.29
4
4
  Summary: Asynchronous Python client for the Violet Pool Controller.
5
- Home-page: https://github.com/Xerolux/violet-poolController-api
6
- Author: Basti (Xerolux)
7
5
  Author-email: "Basti (Xerolux)" <git@xerolux.de>
8
6
  License: AGPL-3.0-or-later
9
- Project-URL: Homepage, https://github.com/Xerolux/violet-poolController-api
10
- Project-URL: Bug Tracker, https://github.com/Xerolux/violet-poolController-api/issues
7
+ Project-URL: Homepage, https://github.com/Xerolux/violet-hass
8
+ Project-URL: Bug Tracker, https://github.com/Xerolux/violet-hass/issues
11
9
  Project-URL: License, https://www.gnu.org/licenses/agpl-3.0.html
12
- Project-URL: Changelog, https://github.com/Xerolux/violet-poolController-api/blob/main/CHANGELOG.md
10
+ Project-URL: Changelog, https://github.com/Xerolux/violet-hass/blob/main/violet_poolcontroller_api/CHANGELOG.md
13
11
  Keywords: pool,violet,controller,home-assistant,async
14
12
  Classifier: Programming Language :: Python :: 3
15
13
  Classifier: Programming Language :: Python :: 3.12
@@ -27,10 +25,7 @@ Provides-Extra: test
27
25
  Requires-Dist: aioresponses>=0.7.8; extra == "test"
28
26
  Requires-Dist: pytest>=8.3; extra == "test"
29
27
  Requires-Dist: pytest-asyncio>=0.24; extra == "test"
30
- Dynamic: author
31
- Dynamic: home-page
32
28
  Dynamic: license-file
33
- Dynamic: requires-python
34
29
 
35
30
  # Violet Pool Controller API
36
31
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "violet-poolController-api"
7
- version = "0.0.26"
7
+ version = "0.0.29"
8
8
  authors = [
9
9
  { name="Basti (Xerolux)", email="git@xerolux.de" },
10
10
  ]
@@ -35,23 +35,23 @@ test = [
35
35
  ]
36
36
 
37
37
  [project.urls]
38
- "Homepage" = "https://github.com/Xerolux/violet-poolController-api"
39
- "Bug Tracker" = "https://github.com/Xerolux/violet-poolController-api/issues"
38
+ "Homepage" = "https://github.com/Xerolux/violet-hass"
39
+ "Bug Tracker" = "https://github.com/Xerolux/violet-hass/issues"
40
40
  "License" = "https://www.gnu.org/licenses/agpl-3.0.html"
41
- "Changelog" = "https://github.com/Xerolux/violet-poolController-api/blob/main/CHANGELOG.md"
41
+ "Changelog" = "https://github.com/Xerolux/violet-hass/blob/main/violet_poolcontroller_api/CHANGELOG.md"
42
42
 
43
43
  [tool.pytest.ini_options]
44
44
  asyncio_mode = "auto"
45
45
 
46
46
  [tool.ruff]
47
47
  line-length = 100
48
- target-version = "0.0.26"
48
+ target-version = "py312"
49
49
 
50
50
  [tool.ruff.lint]
51
51
  select = ["E", "F", "W", "I", "UP"]
52
52
  ignore = ["E501"]
53
53
 
54
54
  [tool.mypy]
55
- python_version = "0.0.26"
55
+ python_version = "3.12"
56
56
  warn_return_any = true
57
57
  warn_unused_configs = true
@@ -1,4 +1,4 @@
1
- # violet-poolController-api - API für Violet Pool Controller
1
+ # violet-poolController-api - API f├╝r Violet Pool Controller
2
2
  # Copyright (C) 2024-2026 Xerolux
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify
@@ -26,6 +26,7 @@ import aiohttp
26
26
  import pytest
27
27
  import pytest_asyncio
28
28
  from aioresponses import aioresponses
29
+ from yarl import URL
29
30
 
30
31
  from violet_poolcontroller_api.api import VioletPoolAPI, VioletPoolAPIError
31
32
  from violet_poolcontroller_api.circuit_breaker import CircuitBreakerOpenError
@@ -483,7 +484,7 @@ async def test_ext1_readings_filtered_when_not_detected(mock_aioresponse, api_cl
483
484
  payload={
484
485
  "getReadings": {
485
486
  "PUMPSTATE": "2",
486
- # No SYSTEM_ext1module_alive_count module not connected
487
+ # No SYSTEM_ext1module_alive_count  module not connected
487
488
  "EXT1_1": 0,
488
489
  "EXT1_2": 0,
489
490
  }
@@ -1179,6 +1180,35 @@ async def test_manual_dosing_stop(
1179
1180
  assert "MANDOS_STOPPED" in result["response"]
1180
1181
 
1181
1182
 
1183
+ @pytest.mark.asyncio
1184
+ async def test_dosing_auto_action_sends_dosstop(
1185
+ mock_aioresponse: aioresponses,
1186
+ api_client: VioletPoolAPI,
1187
+ ) -> None:
1188
+ """AUTO on a dosing output must stop the run, never start one.
1189
+
1190
+ setFunctionManually does not work for DOS_* outputs (PoolDigital forum),
1191
+ so AUTO maps to DOSSTOP via /triggerManualDosing - stopping the manual
1192
+ run returns the channel to automatic mode.
1193
+ """
1194
+ url = "http://192.168.1.100/triggerManualDosing"
1195
+ mock_aioresponse.post(url, body="MANDOS_STOPPED\nOK", status=200)
1196
+
1197
+ result = await api_client.set_switch_state("DOS_6_FLOC", "AUTO")
1198
+
1199
+ assert result["success"] is True
1200
+ request_key = ("POST", URL(url))
1201
+ sent = mock_aioresponse.requests[request_key][0].kwargs["data"]
1202
+ assert sent["action"] == "DOSSTOP"
1203
+
1204
+
1205
+ @pytest.mark.asyncio
1206
+ async def test_dosing_unknown_action_rejected(api_client: VioletPoolAPI) -> None:
1207
+ """Unknown dosing actions raise instead of defaulting to DOSSTART."""
1208
+ with pytest.raises(VioletPoolAPIError, match="Unsupported dosing action"):
1209
+ await api_client.set_switch_state("DOS_1_CL", "PUSH")
1210
+
1211
+
1182
1212
  @pytest.mark.asyncio
1183
1213
  async def test_pv_surplus_with_speed(
1184
1214
  mock_aioresponse: aioresponses,
@@ -1235,3 +1265,124 @@ async def test_pump_off_with_duration(
1235
1265
 
1236
1266
  assert result["success"] is True
1237
1267
  assert result["message"] == "MANUELL AUS\n600 Sekunden"
1268
+
1269
+
1270
+ @pytest.mark.asyncio
1271
+ async def test_pv_surplus_auto_is_mapped_to_off(
1272
+ mock_aioresponse: aioresponses,
1273
+ api_client: VioletPoolAPI,
1274
+ ) -> None:
1275
+ """PVSURPLUS supports only ON/OFF (manual 26.3); AUTO is sent as OFF."""
1276
+ url = "http://192.168.1.100/setFunctionManually?PVSURPLUS,OFF,0,0"
1277
+ mock_aioresponse.get(url, body="OK\nPVSURPLUS\nOFF", status=200)
1278
+
1279
+ result = await api_client.set_switch_state("PVSURPLUS", "AUTO")
1280
+
1281
+ assert result["success"] is True
1282
+ assert result["output"] == "PVSURPLUS"
1283
+
1284
+
1285
+ @pytest.mark.asyncio
1286
+ async def test_pv_surplus_rejects_unknown_action(
1287
+ api_client: VioletPoolAPI,
1288
+ ) -> None:
1289
+ """PVSURPLUS rejects actions that cannot be mapped to ON/OFF."""
1290
+ with pytest.raises(VioletPoolAPIError, match="manual section 26.3"):
1291
+ await api_client.set_switch_state("PVSURPLUS", "PUSH")
1292
+
1293
+
1294
+ @pytest.mark.asyncio
1295
+ async def test_pv_surplus_speed_is_clamped(
1296
+ mock_aioresponse: aioresponses,
1297
+ api_client: VioletPoolAPI,
1298
+ ) -> None:
1299
+ """Pump speed for PVSURPLUS is clamped to the documented 1-3 range."""
1300
+ url = "http://192.168.1.100/setFunctionManually?PVSURPLUS,ON,3,0"
1301
+ mock_aioresponse.get(url, body="OK\nPVSURPLUS\nON", status=200)
1302
+
1303
+ result = await api_client.set_pv_surplus(active=True, pump_speed=5)
1304
+
1305
+ assert result["success"] is True
1306
+
1307
+
1308
+ @pytest.mark.asyncio
1309
+ async def test_client_error_fails_fast_without_retry(
1310
+ mock_aioresponse: aioresponses,
1311
+ api_client: VioletPoolAPI,
1312
+ ) -> None:
1313
+ """A 4xx response raises immediately instead of being retried."""
1314
+ url = "http://192.168.1.100/getReadings?ALL"
1315
+ mock_aioresponse.get(url, status=401, body="Unauthorized")
1316
+
1317
+ with pytest.raises(VioletPoolAPIError, match="HTTP 401"):
1318
+ await api_client.get_readings()
1319
+
1320
+
1321
+ @pytest.mark.asyncio
1322
+ async def test_client_error_does_not_trip_circuit_breaker(
1323
+ mock_aioresponse: aioresponses,
1324
+ api_client: VioletPoolAPI,
1325
+ ) -> None:
1326
+ """Deterministic 4xx errors must not count as circuit breaker failures."""
1327
+ url = "http://192.168.1.100/getReadings?ALL"
1328
+ threshold = api_client._circuit_breaker.failure_threshold
1329
+
1330
+ for _ in range(threshold + 1):
1331
+ mock_aioresponse.get(url, status=401, body="Unauthorized")
1332
+ with pytest.raises(VioletPoolAPIError, match="HTTP 401"):
1333
+ await api_client.get_readings()
1334
+
1335
+ stats = api_client._circuit_breaker.get_stats()
1336
+ assert stats["failure_count"] == 0
1337
+ assert stats["state"] == "CLOSED"
1338
+
1339
+
1340
+ @pytest.mark.asyncio
1341
+ async def test_server_error_still_counts_for_circuit_breaker(
1342
+ mock_aioresponse: aioresponses,
1343
+ api_client: VioletPoolAPI,
1344
+ ) -> None:
1345
+ """5xx errors keep counting as circuit breaker failures."""
1346
+ url = "http://192.168.1.100/getReadings?ALL"
1347
+ mock_aioresponse.get(url, status=500, body="boom")
1348
+
1349
+ with pytest.raises(VioletPoolAPIError):
1350
+ await api_client.get_readings()
1351
+
1352
+ stats = api_client._circuit_breaker.get_stats()
1353
+ assert stats["failure_count"] == 1
1354
+
1355
+
1356
+ def test_command_result_error_first_line() -> None:
1357
+ """Line 1 of the response decides success per manual section 26.2."""
1358
+ result = VioletPoolAPI._command_result("ERROR\nPUMP\nUNKNOWN OUTPUT")
1359
+ assert result["success"] is False
1360
+
1361
+ result = VioletPoolAPI._command_result("OK\nPUMP\nInfo about error handling")
1362
+ assert result["success"] is True
1363
+
1364
+
1365
+ def test_state_translation_language_switch() -> None:
1366
+ """Display texts are language-configurable instead of hardwired German."""
1367
+ from violet_poolcontroller_api.const_devices import (
1368
+ VioletState,
1369
+ get_state_translation_language,
1370
+ set_state_translation_language,
1371
+ )
1372
+
1373
+ assert get_state_translation_language() == "de"
1374
+ state = VioletState("0")
1375
+ assert state.display_mode == "Automatik (Bereit)"
1376
+ assert state.display_mode_for("en") == "Auto (Ready)"
1377
+
1378
+ english_state = VioletState("0", language="en")
1379
+ assert english_state.display_mode == "Auto (Ready)"
1380
+
1381
+ set_state_translation_language("en")
1382
+ try:
1383
+ assert VioletState("4").display_mode == "Manual On"
1384
+ finally:
1385
+ set_state_translation_language("de")
1386
+
1387
+ with pytest.raises(ValueError, match="Unsupported language"):
1388
+ set_state_translation_language("fr")
@@ -7,11 +7,15 @@ credentials, and verifies that authentication is enforced properly.
7
7
  from __future__ import annotations
8
8
 
9
9
  import asyncio
10
+ import socket
10
11
  import subprocess
11
12
  import sys
12
13
  import time
14
+ from collections.abc import Iterator
15
+ from pathlib import Path
13
16
 
14
17
  import aiohttp
18
+ import pytest
15
19
 
16
20
  from violet_poolcontroller_api.api import VioletPoolAPI, VioletPoolAPIError
17
21
 
@@ -20,6 +24,50 @@ PORT = 8499
20
24
  USER = "admin"
21
25
  PASS = "secret"
22
26
 
27
+ _MOCK_SERVER_PATH = Path(__file__).parent / "mock_server.py"
28
+
29
+
30
+ def _start_mock_server() -> subprocess.Popen[bytes]:
31
+ """Start the mock server subprocess and wait until it accepts connections."""
32
+ proc = subprocess.Popen(
33
+ [
34
+ sys.executable,
35
+ str(_MOCK_SERVER_PATH),
36
+ "--port",
37
+ str(PORT),
38
+ "--user",
39
+ USER,
40
+ "--password",
41
+ PASS,
42
+ ],
43
+ stdout=subprocess.DEVNULL,
44
+ stderr=subprocess.DEVNULL,
45
+ )
46
+ deadline = time.monotonic() + 10
47
+ while time.monotonic() < deadline:
48
+ if proc.poll() is not None:
49
+ msg = f"Mock server exited early with code {proc.returncode}"
50
+ raise RuntimeError(msg)
51
+ try:
52
+ with socket.create_connection((HOST, PORT), timeout=0.5):
53
+ return proc
54
+ except OSError:
55
+ time.sleep(0.1)
56
+ proc.terminate()
57
+ msg = f"Mock server did not start listening on port {PORT} within 10s"
58
+ raise RuntimeError(msg)
59
+
60
+
61
+ @pytest.fixture(scope="module", autouse=True)
62
+ def mock_server() -> Iterator[subprocess.Popen[bytes]]:
63
+ """Run the mock server for all tests in this module."""
64
+ proc = _start_mock_server()
65
+ try:
66
+ yield proc
67
+ finally:
68
+ proc.terminate()
69
+ proc.wait(timeout=5)
70
+
23
71
 
24
72
  async def _request(url: str, auth: aiohttp.BasicAuth | None = None) -> tuple[int, str]:
25
73
  async with aiohttp.ClientSession() as session:
@@ -211,12 +259,7 @@ async def test_api_client() -> None:
211
259
 
212
260
  def main() -> None:
213
261
  print(f"Starting mock server on port {PORT} with auth ({USER}:{PASS})...")
214
- proc = subprocess.Popen(
215
- [sys.executable, "tests/mock_server.py", "--port", str(PORT), "--user", USER, "--password", PASS],
216
- stdout=subprocess.DEVNULL,
217
- stderr=subprocess.DEVNULL,
218
- )
219
- time.sleep(2)
262
+ proc = _start_mock_server()
220
263
 
221
264
  try:
222
265
  asyncio.run(test_raw_auth())
@@ -1,15 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: violet-poolController-api
3
- Version: 0.0.26
3
+ Version: 0.0.29
4
4
  Summary: Asynchronous Python client for the Violet Pool Controller.
5
- Home-page: https://github.com/Xerolux/violet-poolController-api
6
- Author: Basti (Xerolux)
7
5
  Author-email: "Basti (Xerolux)" <git@xerolux.de>
8
6
  License: AGPL-3.0-or-later
9
- Project-URL: Homepage, https://github.com/Xerolux/violet-poolController-api
10
- Project-URL: Bug Tracker, https://github.com/Xerolux/violet-poolController-api/issues
7
+ Project-URL: Homepage, https://github.com/Xerolux/violet-hass
8
+ Project-URL: Bug Tracker, https://github.com/Xerolux/violet-hass/issues
11
9
  Project-URL: License, https://www.gnu.org/licenses/agpl-3.0.html
12
- Project-URL: Changelog, https://github.com/Xerolux/violet-poolController-api/blob/main/CHANGELOG.md
10
+ Project-URL: Changelog, https://github.com/Xerolux/violet-hass/blob/main/violet_poolcontroller_api/CHANGELOG.md
13
11
  Keywords: pool,violet,controller,home-assistant,async
14
12
  Classifier: Programming Language :: Python :: 3
15
13
  Classifier: Programming Language :: Python :: 3.12
@@ -27,10 +25,7 @@ Provides-Extra: test
27
25
  Requires-Dist: aioresponses>=0.7.8; extra == "test"
28
26
  Requires-Dist: pytest>=8.3; extra == "test"
29
27
  Requires-Dist: pytest-asyncio>=0.24; extra == "test"
30
- Dynamic: author
31
- Dynamic: home-page
32
28
  Dynamic: license-file
33
- Dynamic: requires-python
34
29
 
35
30
  # Violet Pool Controller API
36
31
 
@@ -1,10 +1,6 @@
1
1
  LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
- setup.py
5
- tests/__init__.py
6
- tests/conftest.py
7
- tests/mock_server.py
8
4
  tests/test_api.py
9
5
  tests/test_api_smoke.py
10
6
  tests/test_mock_server.py
@@ -18,5 +14,7 @@ violet_poolcontroller_api/api.py
18
14
  violet_poolcontroller_api/circuit_breaker.py
19
15
  violet_poolcontroller_api/const_api.py
20
16
  violet_poolcontroller_api/const_devices.py
17
+ violet_poolcontroller_api/parsers.py
18
+ violet_poolcontroller_api/readings.py
21
19
  violet_poolcontroller_api/utils_rate_limiter.py
22
20
  violet_poolcontroller_api/utils_sanitizer.py
@@ -1,72 +1,135 @@
1
- # violet-poolController-api - API für Violet Pool Controller
2
- # Copyright (C) 2024-2026 Xerolux
3
- #
4
- # This program is free software: you can redistribute it and/or modify
5
- # it under the terms of the GNU Affero General Public License as published
6
- # by the Free Software Foundation, either version 3 of the License, or
7
- # (at your option) any later version.
8
- #
9
- # This program is distributed in the hope that it will be useful,
10
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
- # GNU Affero General Public License for more details.
13
- #
14
- # You should have received a copy of the GNU Affero General Public License
15
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
16
-
17
- """Violet Pool Controller API client library."""
18
-
19
- from .api import VioletPoolAPI, VioletPoolAPIError
20
- from .circuit_breaker import CircuitBreaker, CircuitBreakerOpenError
21
- from .const_api import ( # noqa: F401
22
- ACTION_ALLAUTO,
23
- ACTION_ALLOFF,
24
- ACTION_ALLON,
25
- ACTION_AUTO,
26
- ACTION_COLOR,
27
- ACTION_LOCK,
28
- ACTION_OFF,
29
- ACTION_ON,
30
- ACTION_PUSH,
31
- ACTION_UNLOCK,
32
- ERROR_CODES,
33
- ERROR_SEVERITY_ALARM,
34
- ERROR_SEVERITY_INFO,
35
- ERROR_SEVERITY_WARNING,
36
- )
37
- from .const_devices import ( # noqa: F401
38
- COVER_FUNCTIONS,
39
- COVER_STATE_MAP,
40
- DEVICE_PARAMETERS,
41
- VioletState,
42
- )
43
- from .utils_rate_limiter import RateLimiter, get_global_rate_limiter
44
- from .utils_sanitizer import InputSanitizer
45
-
46
- __all__ = [
47
- "VioletPoolAPI",
48
- "VioletPoolAPIError",
49
- "CircuitBreaker",
50
- "CircuitBreakerOpenError",
51
- "VioletState",
52
- "InputSanitizer",
53
- "RateLimiter",
54
- "get_global_rate_limiter",
55
- "ACTION_ALLAUTO",
56
- "ACTION_ALLOFF",
57
- "ACTION_ALLON",
58
- "ACTION_AUTO",
59
- "ACTION_COLOR",
60
- "ACTION_LOCK",
61
- "ACTION_OFF",
62
- "ACTION_ON",
63
- "ACTION_PUSH",
64
- "ACTION_UNLOCK",
65
- "COVER_FUNCTIONS",
66
- "COVER_STATE_MAP",
67
- "DEVICE_PARAMETERS",
68
- "ERROR_CODES",
69
- "ERROR_SEVERITY_ALARM",
70
- "ERROR_SEVERITY_INFO",
71
- "ERROR_SEVERITY_WARNING",
72
- ]
1
+ # violet-poolController-api - API f├╝r Violet Pool Controller
2
+ # Copyright (C) 2024-2026 Xerolux
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as published
6
+ # by the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
16
+
17
+ """Violet Pool Controller API client library."""
18
+
19
+ from .api import (
20
+ SETPOINT_RANGES,
21
+ VioletAuthError,
22
+ VioletPayloadError,
23
+ VioletPoolAPI,
24
+ VioletPoolAPIError,
25
+ VioletSetpointError,
26
+ VioletTimeoutError,
27
+ VioletUnsafeOperationError,
28
+ validate_setpoint,
29
+ )
30
+ from .circuit_breaker import CircuitBreaker, CircuitBreakerOpenError
31
+ from .const_api import ( # noqa: F401
32
+ ACTION_ALLAUTO,
33
+ ACTION_ALLOFF,
34
+ ACTION_ALLON,
35
+ ACTION_AUTO,
36
+ ACTION_COLOR,
37
+ ACTION_LOCK,
38
+ ACTION_OFF,
39
+ ACTION_ON,
40
+ ACTION_PUSH,
41
+ ACTION_UNLOCK,
42
+ ERROR_CODES,
43
+ ERROR_SEVERITY_ALARM,
44
+ ERROR_SEVERITY_INFO,
45
+ ERROR_SEVERITY_WARNING,
46
+ )
47
+ from .const_devices import ( # noqa: F401
48
+ COVER_FUNCTIONS,
49
+ COVER_STATE_MAP,
50
+ DEVICE_PARAMETERS,
51
+ STATE_TRANSLATIONS,
52
+ CoverState,
53
+ DmxSceneState,
54
+ OnewireState,
55
+ OutputState,
56
+ PvSurplusState,
57
+ RuleState,
58
+ VioletState,
59
+ get_state_translation_language,
60
+ set_state_translation_language,
61
+ )
62
+ from .parsers import ( # noqa: F401
63
+ parse_epoch_milliseconds,
64
+ parse_epoch_seconds,
65
+ parse_hms_string,
66
+ parse_optional_seconds,
67
+ parse_runtime_string,
68
+ parse_uptime_string,
69
+ )
70
+ from .readings import VioletReadings
71
+ from .utils_rate_limiter import RateLimiter, get_global_rate_limiter
72
+ from .utils_sanitizer import InputSanitizer
73
+
74
+ __all__ = [
75
+ # Core client
76
+ "VioletPoolAPI",
77
+ # Exception hierarchy
78
+ "VioletPoolAPIError",
79
+ "VioletAuthError",
80
+ "VioletTimeoutError",
81
+ "VioletPayloadError",
82
+ "VioletSetpointError",
83
+ "VioletUnsafeOperationError",
84
+ # Setpoint validation
85
+ "SETPOINT_RANGES",
86
+ "validate_setpoint",
87
+ # Circuit breaker
88
+ "CircuitBreaker",
89
+ "CircuitBreakerOpenError",
90
+ # Enums
91
+ "OutputState",
92
+ "DmxSceneState",
93
+ "RuleState",
94
+ "CoverState",
95
+ "OnewireState",
96
+ "PvSurplusState",
97
+ # State helpers
98
+ "VioletState",
99
+ "STATE_TRANSLATIONS",
100
+ "get_state_translation_language",
101
+ "set_state_translation_language",
102
+ # Typed readings snapshot
103
+ "VioletReadings",
104
+ # Parsers
105
+ "parse_runtime_string",
106
+ "parse_hms_string",
107
+ "parse_uptime_string",
108
+ "parse_epoch_seconds",
109
+ "parse_epoch_milliseconds",
110
+ "parse_optional_seconds",
111
+ # Utilities
112
+ "InputSanitizer",
113
+ "RateLimiter",
114
+ "get_global_rate_limiter",
115
+ # Action constants
116
+ "ACTION_ALLAUTO",
117
+ "ACTION_ALLOFF",
118
+ "ACTION_ALLON",
119
+ "ACTION_AUTO",
120
+ "ACTION_COLOR",
121
+ "ACTION_LOCK",
122
+ "ACTION_OFF",
123
+ "ACTION_ON",
124
+ "ACTION_PUSH",
125
+ "ACTION_UNLOCK",
126
+ # Device constants
127
+ "COVER_FUNCTIONS",
128
+ "COVER_STATE_MAP",
129
+ "DEVICE_PARAMETERS",
130
+ # Error codes
131
+ "ERROR_CODES",
132
+ "ERROR_SEVERITY_ALARM",
133
+ "ERROR_SEVERITY_INFO",
134
+ "ERROR_SEVERITY_WARNING",
135
+ ]