violet-poolController-api 0.0.22__tar.gz → 0.0.24__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 (20) hide show
  1. {violet_poolcontroller_api-0.0.22/violet_poolController_api.egg-info → violet_poolcontroller_api-0.0.24}/PKG-INFO +1 -1
  2. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/pyproject.toml +3 -3
  3. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/setup.py +3 -2
  4. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/tests/test_api.py +16 -2
  5. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24/violet_poolController_api.egg-info}/PKG-INFO +1 -1
  6. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/violet_poolcontroller_api/api.py +121 -9
  7. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/violet_poolcontroller_api/const_api.py +18 -5
  8. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/LICENSE +0 -0
  9. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/README.md +0 -0
  10. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/setup.cfg +0 -0
  11. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/tests/__init__.py +0 -0
  12. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/violet_poolController_api.egg-info/SOURCES.txt +0 -0
  13. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/violet_poolController_api.egg-info/dependency_links.txt +0 -0
  14. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/violet_poolController_api.egg-info/requires.txt +0 -0
  15. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/violet_poolController_api.egg-info/top_level.txt +0 -0
  16. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/violet_poolcontroller_api/__init__.py +0 -0
  17. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/violet_poolcontroller_api/circuit_breaker.py +0 -0
  18. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/violet_poolcontroller_api/const_devices.py +0 -0
  19. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/violet_poolcontroller_api/utils_rate_limiter.py +0 -0
  20. {violet_poolcontroller_api-0.0.22 → violet_poolcontroller_api-0.0.24}/violet_poolcontroller_api/utils_sanitizer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: violet-poolController-api
3
- Version: 0.0.22
3
+ Version: 0.0.24
4
4
  Summary: Asynchronous Python client for the Violet Pool Controller.
5
5
  Home-page: https://github.com/Xerolux/violet-poolController-api
6
6
  Author: Basti (Xerolux)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "violet-poolController-api"
7
- version = "0.0.22"
7
+ version = "0.0.24"
8
8
  authors = [
9
9
  { name="Basti (Xerolux)", email="git@xerolux.de" },
10
10
  ]
@@ -45,13 +45,13 @@ asyncio_mode = "auto"
45
45
 
46
46
  [tool.ruff]
47
47
  line-length = 100
48
- target-version = "0.0.22"
48
+ target-version = "0.0.24"
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.22"
55
+ python_version = "0.0.24"
56
56
  warn_return_any = true
57
57
  warn_unused_configs = true
@@ -1,7 +1,8 @@
1
- from setuptools import setup, find_packages
1
+ from setuptools import find_packages, setup
2
+
2
3
  setup(
3
4
  name="violet-poolController-api",
4
- version="0.0.22",
5
+ version="0.0.24",
5
6
  author="Basti (Xerolux)",
6
7
  author_email="git@xerolux.de",
7
8
  description="Asynchronous Python client for the Violet Pool Controller.",
@@ -784,6 +784,20 @@ async def test_set_device_temperature(
784
784
  assert result["success"] is True
785
785
 
786
786
 
787
+ @pytest.mark.asyncio
788
+ async def test_set_device_temperature_solar(
789
+ mock_aioresponse: aioresponses,
790
+ api_client: VioletPoolAPI,
791
+ ) -> None:
792
+ """Test set_device_temperature uses SOLAR_maxtemp for SOLAR key."""
793
+ url = "http://192.168.1.100/setConfig"
794
+ mock_aioresponse.post(url, body="OK", status=200)
795
+
796
+ result = await api_client.set_device_temperature("SOLAR", 30.0)
797
+
798
+ assert result["success"] is True
799
+
800
+
787
801
  @pytest.mark.asyncio
788
802
  async def test_set_ph_target(
789
803
  mock_aioresponse: aioresponses,
@@ -845,8 +859,8 @@ async def test_set_dosing_parameters(
845
859
  mock_aioresponse: aioresponses,
846
860
  api_client: VioletPoolAPI,
847
861
  ) -> None:
848
- """Test set_dosing_parameters sends POST with dosing configuration."""
849
- url = "http://192.168.1.100/setDosingParameters"
862
+ """Test set_dosing_parameters sends POST via /setConfig."""
863
+ url = "http://192.168.1.100/setConfig"
850
864
  mock_aioresponse.post(url, body="OK", status=200)
851
865
 
852
866
  result = await api_client.set_dosing_parameters({"DOS_1_CL_DOSING_TIME": 30})
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: violet-poolController-api
3
- Version: 0.0.22
3
+ Version: 0.0.24
4
4
  Summary: Asynchronous Python client for the Violet Pool Controller.
5
5
  Home-page: https://github.com/Xerolux/violet-poolController-api
6
6
  Author: Basti (Xerolux)
@@ -42,6 +42,8 @@ from .const_api import (
42
42
  API_GET_CALIB_RAW_VALUES,
43
43
  API_GET_CONFIG,
44
44
  API_GET_HISTORY,
45
+ API_GET_LOG,
46
+ API_GET_NOTIFICATIONS,
45
47
  API_GET_OUTPUT_STATES,
46
48
  API_GET_OVERALL_DOSING,
47
49
  API_GET_WEATHER_DATA,
@@ -50,10 +52,10 @@ from .const_api import (
50
52
  API_READINGS,
51
53
  API_RESTORE_CALIBRATION,
52
54
  API_SET_CONFIG,
53
- API_SET_DOSING_PARAMETERS,
54
55
  API_SET_FUNCTION_MANUALLY,
55
56
  API_SET_OUTPUT_TESTMODE,
56
57
  API_TRIGGER_MANUAL_DOSING,
58
+ DOSING_CONFIG_PREFIX,
57
59
  DOSING_FUNCTIONS,
58
60
  DOSING_OUTPUT_INDEX,
59
61
  ERROR_CODES,
@@ -1117,8 +1119,8 @@ class VioletPoolAPI:
1117
1119
  A dictionary with the command result.
1118
1120
 
1119
1121
  """
1120
- target_key = f"{climate_key}_TARGET_TEMP"
1121
- return await self.set_target_value(target_key, float(temperature))
1122
+ config_key = "SOLAR_maxtemp" if climate_key.upper() == "SOLAR" else f"{climate_key}_set_temp"
1123
+ return await self.set_target_value(config_key, float(temperature))
1122
1124
 
1123
1125
  async def set_ph_target(self, value: float) -> dict[str, Any]:
1124
1126
  """Update the pH setpoint.
@@ -1173,7 +1175,11 @@ class VioletPoolAPI:
1173
1175
  self,
1174
1176
  parameters: Mapping[str, Any],
1175
1177
  ) -> dict[str, Any]:
1176
- """Update dosing parameters via the dedicated endpoint.
1178
+ """Update dosing parameters via /setConfig.
1179
+
1180
+ The /setDosingParameters endpoint does not exist on the controller
1181
+ (firmware 1.1.9). All dosing parameters are written through
1182
+ POST /setConfig, just like other configuration values.
1177
1183
 
1178
1184
  Args:
1179
1185
  parameters: A mapping of dosing parameters.
@@ -1182,12 +1188,64 @@ class VioletPoolAPI:
1182
1188
  A dictionary with the command result.
1183
1189
 
1184
1190
  """
1185
- body = await self._request(
1186
- API_SET_DOSING_PARAMETERS,
1187
- method="POST",
1188
- data=dict(parameters),
1191
+ return await self.set_config(dict(parameters))
1192
+
1193
+ async def set_dosage_enabled(
1194
+ self,
1195
+ dosing_type: str,
1196
+ enabled: bool,
1197
+ ) -> dict[str, Any]:
1198
+ """Enable or disable a dosing function.
1199
+
1200
+ Args:
1201
+ dosing_type: One of ``"pH-"``, ``"pH+"``, ``"Chlor"``,
1202
+ ``"Elektrolyse"``, ``"Flockmittel"``, ``"H2O2"``.
1203
+ enabled: True to enable, False to disable.
1204
+
1205
+ Returns:
1206
+ A dictionary with the command result.
1207
+
1208
+ Raises:
1209
+ VioletPoolAPIError: If the dosing type is unknown.
1210
+
1211
+ """
1212
+ prefix = DOSING_CONFIG_PREFIX.get(dosing_type)
1213
+ if prefix is None:
1214
+ msg = (
1215
+ f"Unknown dosing type '{dosing_type}'. "
1216
+ f"Valid: {list(DOSING_CONFIG_PREFIX)}"
1217
+ )
1218
+ raise VioletPoolAPIError(msg)
1219
+
1220
+ return await self.set_config({f"{prefix}_use": 1 if enabled else 0})
1221
+
1222
+ async def is_dosage_enabled(self, dosing_type: str) -> bool:
1223
+ """Check whether a dosing function is enabled.
1224
+
1225
+ Args:
1226
+ dosing_type: One of ``"pH-"``, ``"pH+"``, ``"Chlor"``,
1227
+ ``"Elektrolyse"``, ``"Flockmittel"``, ``"H2O2"``.
1228
+
1229
+ Returns:
1230
+ True if the dosing function is enabled.
1231
+
1232
+ Raises:
1233
+ VioletPoolAPIError: If the dosing type is unknown.
1234
+
1235
+ """
1236
+ prefix = DOSING_CONFIG_PREFIX.get(dosing_type)
1237
+ if prefix is None:
1238
+ msg = (
1239
+ f"Unknown dosing type '{dosing_type}'. "
1240
+ f"Valid: {list(DOSING_CONFIG_PREFIX)}"
1241
+ )
1242
+ raise VioletPoolAPIError(msg)
1243
+
1244
+ result = await self._request_json_dict(
1245
+ API_GET_CONFIG,
1246
+ query=f"{prefix}_use",
1189
1247
  )
1190
- return self._command_result(body)
1248
+ return bool(int(result.get(f"{prefix}_use", 0)))
1191
1249
 
1192
1250
  async def set_pump_speed(
1193
1251
  self,
@@ -1307,3 +1365,57 @@ class VioletPoolAPI:
1307
1365
  )
1308
1366
 
1309
1367
  return results
1368
+
1369
+ async def get_log(
1370
+ self,
1371
+ log_type: str,
1372
+ page: int = 0,
1373
+ ) -> dict[str, Any]:
1374
+ """Fetch log entries from the controller.
1375
+
1376
+ Args:
1377
+ log_type: One of ``LOG_TYPE_ACTIONS``, ``LOG_TYPE_SWITCHING``,
1378
+ ``LOG_TYPE_ONEWIRE`` (``"actions"``, ``"switching"``,
1379
+ ``"onewire"``).
1380
+ page: Page number (0-based). Use -1 to download the full
1381
+ actions log instead of paginated text.
1382
+
1383
+ Returns:
1384
+ A dict with keys:
1385
+ - ``lines``: list of pipe-delimited log line strings
1386
+ - ``has_more``: True when ``LOAD_MORE`` sentinel was present
1387
+ - ``raw``: the raw text response
1388
+
1389
+ """
1390
+ if page < 0 and log_type == "actions":
1391
+ url = f"{API_GET_LOG}?downloadActionsLog"
1392
+ else:
1393
+ url = f"{API_GET_LOG}?{log_type}&{page}"
1394
+
1395
+ resp = await self._api_request(
1396
+ "GET",
1397
+ url,
1398
+ priority=API_PRIORITY_NORMAL,
1399
+ )
1400
+ text = resp.strip() if resp else ""
1401
+ lines = text.split("\n") if text else []
1402
+ has_more = lines and lines[-1].strip() == "LOAD_MORE"
1403
+ if has_more:
1404
+ lines = lines[:-1]
1405
+ lines = [ln for ln in lines if ln.strip()]
1406
+ return {"lines": lines, "has_more": has_more, "raw": text}
1407
+
1408
+ async def get_notifications(self) -> dict[str, Any]:
1409
+ """Fetch all notification history from the controller.
1410
+
1411
+ Returns:
1412
+ The JSON response dict where each key is a numeric ID and each
1413
+ value is a notification record with fields like DATE, TIME,
1414
+ SENSOR_ID, TYPE, TEXT, MAIL_STATE, etc.
1415
+
1416
+ """
1417
+ return await self._api_request(
1418
+ "GET",
1419
+ f"{API_GET_NOTIFICATIONS}?ALL",
1420
+ priority=API_PRIORITY_NORMAL,
1421
+ )
@@ -31,8 +31,6 @@ from __future__ import annotations
31
31
 
32
32
  API_READINGS = "/getReadings"
33
33
  API_SET_FUNCTION_MANUALLY = "/setFunctionManually"
34
- API_SET_DOSING_PARAMETERS = "/setDosingParameters"
35
- API_SET_TARGET_VALUES = "/setTargetValues"
36
34
  API_GET_CONFIG = "/getConfig"
37
35
  API_SET_CONFIG = "/setConfig"
38
36
  API_GET_CALIB_RAW_VALUES = "/getCalibRawValues"
@@ -44,6 +42,12 @@ API_GET_HISTORY = "/getHistory"
44
42
  API_GET_WEATHER_DATA = "/getWeatherdata"
45
43
  API_GET_OVERALL_DOSING = "/getOverallDosing"
46
44
  API_GET_OUTPUT_STATES = "/getOutputstates"
45
+ API_GET_LOG = "/getLog"
46
+ API_GET_NOTIFICATIONS = "/getNotifications"
47
+
48
+ LOG_TYPE_ACTIONS = "actions"
49
+ LOG_TYPE_SWITCHING = "switching"
50
+ LOG_TYPE_ONEWIRE = "onewire"
47
51
 
48
52
  # Settings for optimizing data refreshes by fetching specific groups.
49
53
  SPECIFIC_READING_GROUPS = (
@@ -80,9 +84,9 @@ ACTION_UNLOCK = "UNLOCK"
80
84
 
81
85
  # Common Query and Target Parameters
82
86
  QUERY_ALL = "ALL"
83
- TARGET_PH = "pH"
84
- TARGET_ORP = "ORP"
85
- TARGET_MIN_CHLORINE = "MinChlorine"
87
+ TARGET_PH = "DOSAGE_phminus_setpoint"
88
+ TARGET_ORP = "DOSAGE_chlorine_setpoint_orp"
89
+ TARGET_MIN_CHLORINE = "DOSAGE_chlorine_lowerval_cl"
86
90
  KEY_MAINTENANCE = "MAINTENANCE"
87
91
  KEY_PVSURPLUS = "PVSURPLUS"
88
92
 
@@ -161,6 +165,15 @@ DOSING_OUTPUT_INDEX = {
161
165
  "DOS_6_FLOC": 5,
162
166
  }
163
167
 
168
+ DOSING_CONFIG_PREFIX = {
169
+ "pH-": "DOSAGE_phminus",
170
+ "pH+": "DOSAGE_phplus",
171
+ "Chlor": "DOSAGE_chlorine",
172
+ "Elektrolyse": "DOSAGE_electrolysis",
173
+ "Flockmittel": "DOSAGE_floc",
174
+ "H2O2": "DOSAGE_h2o2",
175
+ }
176
+
164
177
  # =============================================================================
165
178
  # CONTROLLER ERROR CODES (Manual Section 27.2 - Software 1.1.9)
166
179
  # =============================================================================