violet-poolController-api 0.0.39__tar.gz → 0.0.40__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.
- {violet_poolcontroller_api-0.0.39/violet_poolController_api.egg-info → violet_poolcontroller_api-0.0.40}/PKG-INFO +1 -1
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/pyproject.toml +1 -1
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_api.py +38 -18
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_mock_server.py +16 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40/violet_poolController_api.egg-info}/PKG-INFO +1 -1
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/_api_readings.py +11 -2
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/const_api.py +8 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/LICENSE +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/README.md +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/setup.cfg +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_api_smoke.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_circuit_breaker.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_language_policy.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_parsers.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_rate_limiter.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_readings.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_sanitizer.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolController_api.egg-info/SOURCES.txt +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolController_api.egg-info/dependency_links.txt +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolController_api.egg-info/requires.txt +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolController_api.egg-info/top_level.txt +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/__init__.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/_api_dosing.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/_api_mixin.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/_api_model.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/_api_outputs.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/_api_system.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/api.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/circuit_breaker.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/const_devices.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/parsers.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/py.typed +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/readings.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/utils_rate_limiter.py +0 -0
- {violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/violet_poolcontroller_api/utils_sanitizer.py +0 -0
|
@@ -42,6 +42,7 @@ from violet_poolcontroller_api.const_api import (
|
|
|
42
42
|
ERROR_SEVERITY_INFO,
|
|
43
43
|
ERROR_SEVERITY_REMINDER,
|
|
44
44
|
ERROR_SEVERITY_WARNING,
|
|
45
|
+
QUERY_FULL_REFRESH,
|
|
45
46
|
TARGET_PH,
|
|
46
47
|
)
|
|
47
48
|
|
|
@@ -92,7 +93,7 @@ async def test_get_readings_success(
|
|
|
92
93
|
api_client: VioletPoolAPI,
|
|
93
94
|
) -> None:
|
|
94
95
|
"""Test get_readings returns the correct parsed JSON dictionary."""
|
|
95
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
96
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
96
97
|
mock_data = {"PUMPSTATE": "2", "PH": 7.2}
|
|
97
98
|
mock_aioresponse.get(url, payload=mock_data, status=200)
|
|
98
99
|
|
|
@@ -102,6 +103,25 @@ async def test_get_readings_success(
|
|
|
102
103
|
assert dict(result) == mock_data
|
|
103
104
|
|
|
104
105
|
|
|
106
|
+
@pytest.mark.asyncio
|
|
107
|
+
async def test_get_readings_requests_all_feature_flags(
|
|
108
|
+
mock_aioresponse: aioresponses,
|
|
109
|
+
api_client: VioletPoolAPI,
|
|
110
|
+
) -> None:
|
|
111
|
+
"""Plain ``ALL`` omits the computed fields (daily dosing totals, runtime
|
|
112
|
+
strings, priority-state composites) on some firmware states, leaving the
|
|
113
|
+
matching sensors at ``unknown``. get_readings() must therefore always
|
|
114
|
+
ask for ``ALL`` combined with every feature-flag token."""
|
|
115
|
+
url = f"http://192.168.1.100/getReadings?{QUERY_FULL_REFRESH}"
|
|
116
|
+
mock_aioresponse.get(url, payload={"PUMPSTATE": "2"}, status=200)
|
|
117
|
+
|
|
118
|
+
await api_client.get_readings()
|
|
119
|
+
|
|
120
|
+
# aioresponses matches the URL exactly; reaching this point without an
|
|
121
|
+
# unsatisfied-mock error proves the full query string was sent.
|
|
122
|
+
assert mock_aioresponse.requests
|
|
123
|
+
|
|
124
|
+
|
|
105
125
|
@pytest.mark.asyncio
|
|
106
126
|
async def test_get_output_runtimes_success(
|
|
107
127
|
mock_aioresponse: aioresponses,
|
|
@@ -164,7 +184,7 @@ async def test_request_server_error(
|
|
|
164
184
|
api_client: VioletPoolAPI,
|
|
165
185
|
) -> None:
|
|
166
186
|
"""Test that a 500 error raises VioletPoolAPIError after retrying."""
|
|
167
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
187
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
168
188
|
mock_aioresponse.get(url, status=500)
|
|
169
189
|
# the second time it retries
|
|
170
190
|
mock_aioresponse.get(url, status=500)
|
|
@@ -358,7 +378,7 @@ async def test_get_readings_standalone_list_format(
|
|
|
358
378
|
api_client: VioletPoolAPI,
|
|
359
379
|
) -> None:
|
|
360
380
|
"""Test get_readings parses the standalone list format correctly."""
|
|
361
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
381
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
362
382
|
mock_data = {
|
|
363
383
|
"getReadings": [
|
|
364
384
|
{
|
|
@@ -394,7 +414,7 @@ async def test_dosing_standalone_detection_dict_format(
|
|
|
394
414
|
standalone_api_client: VioletPoolAPI,
|
|
395
415
|
) -> None:
|
|
396
416
|
"""Test dosing_standalone is set to False when dict format is received."""
|
|
397
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
417
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
398
418
|
mock_data = {
|
|
399
419
|
"getReadings": {
|
|
400
420
|
"PUMPSTATE": "2",
|
|
@@ -414,7 +434,7 @@ async def test_dosing_standalone_detection_dict_format(
|
|
|
414
434
|
@pytest.mark.asyncio
|
|
415
435
|
async def test_get_hardware_profile(mock_aioresponse, api_client):
|
|
416
436
|
"""Test get_hardware_profile correctly detects components via alive counters."""
|
|
417
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
437
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
418
438
|
|
|
419
439
|
# 1. Base module only (no DOS, EXT)
|
|
420
440
|
mock_aioresponse.get(
|
|
@@ -524,7 +544,7 @@ async def test_module_alive_on_zero_count(mock_aioresponse, api_client):
|
|
|
524
544
|
old code required value > 0, which caused EXT1_* readings to be filtered
|
|
525
545
|
and the relay switch to appear broken right after a restart.
|
|
526
546
|
"""
|
|
527
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
547
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
528
548
|
mock_aioresponse.get(
|
|
529
549
|
url,
|
|
530
550
|
payload={
|
|
@@ -548,7 +568,7 @@ async def test_module_alive_on_zero_count(mock_aioresponse, api_client):
|
|
|
548
568
|
@pytest.mark.asyncio
|
|
549
569
|
async def test_ext1_readings_not_filtered_when_detected(mock_aioresponse, api_client):
|
|
550
570
|
"""EXT1_* readings are included when extension_module_1 is detected."""
|
|
551
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
571
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
552
572
|
mock_aioresponse.get(
|
|
553
573
|
url,
|
|
554
574
|
payload={
|
|
@@ -572,7 +592,7 @@ async def test_ext1_readings_not_filtered_when_detected(mock_aioresponse, api_cl
|
|
|
572
592
|
@pytest.mark.asyncio
|
|
573
593
|
async def test_ext1_readings_filtered_when_not_detected(mock_aioresponse, api_client):
|
|
574
594
|
"""EXT1_* readings are stripped when extension_module_1 key is absent."""
|
|
575
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
595
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
576
596
|
mock_aioresponse.get(
|
|
577
597
|
url,
|
|
578
598
|
payload={
|
|
@@ -594,7 +614,7 @@ async def test_ext1_readings_filtered_when_not_detected(mock_aioresponse, api_cl
|
|
|
594
614
|
@pytest.mark.asyncio
|
|
595
615
|
async def test_get_hardware_profile_standalone_dosing(mock_aioresponse, standalone_api_client):
|
|
596
616
|
"""Test get_hardware_profile with a standalone dosing configuration."""
|
|
597
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
617
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
598
618
|
# Using the standalone list format
|
|
599
619
|
mock_data = {
|
|
600
620
|
"getReadings": [
|
|
@@ -1874,7 +1894,7 @@ async def test_client_error_fails_fast_without_retry(
|
|
|
1874
1894
|
api_client: VioletPoolAPI,
|
|
1875
1895
|
) -> None:
|
|
1876
1896
|
"""A 4xx response raises immediately instead of being retried."""
|
|
1877
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
1897
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
1878
1898
|
mock_aioresponse.get(url, status=401, body="Unauthorized")
|
|
1879
1899
|
|
|
1880
1900
|
with pytest.raises(VioletPoolAPIError, match="HTTP 401"):
|
|
@@ -1887,7 +1907,7 @@ async def test_client_error_does_not_trip_circuit_breaker(
|
|
|
1887
1907
|
api_client: VioletPoolAPI,
|
|
1888
1908
|
) -> None:
|
|
1889
1909
|
"""Deterministic 4xx errors must not count as circuit breaker failures."""
|
|
1890
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
1910
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
1891
1911
|
threshold = api_client._circuit_breaker.failure_threshold
|
|
1892
1912
|
|
|
1893
1913
|
for _ in range(threshold + 1):
|
|
@@ -1907,7 +1927,7 @@ async def test_empty_get_readings_payload_is_flattened(
|
|
|
1907
1927
|
mock_aioresponse: aioresponses,
|
|
1908
1928
|
api_client: VioletPoolAPI,
|
|
1909
1929
|
) -> None:
|
|
1910
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
1930
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
1911
1931
|
mock_aioresponse.get(url, payload={"getReadings": {}}, status=200)
|
|
1912
1932
|
|
|
1913
1933
|
readings = await api_client.get_readings()
|
|
@@ -1937,7 +1957,7 @@ async def test_basic_auth_uses_authorization_header(
|
|
|
1937
1957
|
mock_aioresponse: aioresponses,
|
|
1938
1958
|
api_client: VioletPoolAPI,
|
|
1939
1959
|
) -> None:
|
|
1940
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
1960
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
1941
1961
|
mock_aioresponse.get(url, payload={}, status=200)
|
|
1942
1962
|
|
|
1943
1963
|
await api_client.get_readings()
|
|
@@ -1953,7 +1973,7 @@ async def test_server_error_still_counts_for_circuit_breaker(
|
|
|
1953
1973
|
api_client: VioletPoolAPI,
|
|
1954
1974
|
) -> None:
|
|
1955
1975
|
"""5xx errors keep counting as circuit breaker failures."""
|
|
1956
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
1976
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
1957
1977
|
mock_aioresponse.get(url, status=500, body="boom", repeat=True)
|
|
1958
1978
|
|
|
1959
1979
|
with pytest.raises(VioletPoolAPIError):
|
|
@@ -1976,7 +1996,7 @@ async def test_rate_limiter_reacquired_on_every_retry(
|
|
|
1976
1996
|
retry loop, so retries fired real HTTP requests without ever going
|
|
1977
1997
|
through the limiter again.
|
|
1978
1998
|
"""
|
|
1979
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
1999
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
1980
2000
|
mock_aioresponse.get(url, status=500, body="boom", repeat=True)
|
|
1981
2001
|
# Backoff sleeps aren't what this test verifies; skip them for speed.
|
|
1982
2002
|
monkeypatch.setattr("violet_poolcontroller_api.api.asyncio.sleep", AsyncMock())
|
|
@@ -2259,7 +2279,7 @@ async def test_reads_are_still_retried(
|
|
|
2259
2279
|
mock_aioresponse: aioresponses,
|
|
2260
2280
|
) -> None:
|
|
2261
2281
|
"""The read path keeps its retries; repeating a read costs nothing."""
|
|
2262
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
2282
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
2263
2283
|
mock_aioresponse.get(url, status=500, body="boom")
|
|
2264
2284
|
mock_aioresponse.get(url, status=500, body="boom")
|
|
2265
2285
|
mock_aioresponse.get(url, payload={"getReadings": {"PUMP": "1"}}, status=200)
|
|
@@ -2282,7 +2302,7 @@ async def test_rate_limit_wait_timeout_fails_instead_of_bypassing(
|
|
|
2282
2302
|
Sending anyway let every caller that waited out the timeout hit the
|
|
2283
2303
|
controller at once, which is the pile-up the limiter exists to prevent.
|
|
2284
2304
|
"""
|
|
2285
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
2305
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
2286
2306
|
mock_aioresponse.get(url, payload={"getReadings": {}}, status=200)
|
|
2287
2307
|
|
|
2288
2308
|
async with aiohttp.ClientSession() as session:
|
|
@@ -2308,7 +2328,7 @@ async def test_html_login_page_does_not_open_the_circuit_breaker(
|
|
|
2308
2328
|
Counting those as transient failures opened the breaker and replaced the
|
|
2309
2329
|
payload error that explains the actual problem.
|
|
2310
2330
|
"""
|
|
2311
|
-
url = "http://192.168.1.100/getReadings?ALL"
|
|
2331
|
+
url = "http://192.168.1.100/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
2312
2332
|
for _ in range(8):
|
|
2313
2333
|
mock_aioresponse.get(url, body="<html>login</html>", status=200)
|
|
2314
2334
|
|
{violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_mock_server.py
RENAMED
|
@@ -8,6 +8,7 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
import asyncio
|
|
10
10
|
import base64
|
|
11
|
+
import json
|
|
11
12
|
import socket
|
|
12
13
|
import subprocess
|
|
13
14
|
import sys
|
|
@@ -89,6 +90,21 @@ async def test_raw_auth() -> None:
|
|
|
89
90
|
assert status == 200, f"Expected 200, got {status}"
|
|
90
91
|
print(f" OK: status={status} body_len={len(body)}")
|
|
91
92
|
|
|
93
|
+
print()
|
|
94
|
+
print("=" * 60)
|
|
95
|
+
print("TEST 1b: ALL plus feature-flag tokens must not filter the payload")
|
|
96
|
+
print("=" * 60)
|
|
97
|
+
status_mixed, body_mixed = await _request(
|
|
98
|
+
f"http://{HOST}:{PORT}/getReadings?ALL,DOSAGE,RUNTIMES,PUMPPRIOSTATE,BACKWASH,SYSTEM"
|
|
99
|
+
)
|
|
100
|
+
assert status_mixed == 200, f"Expected 200, got {status_mixed}"
|
|
101
|
+
# build_readings() re-randomizes sensor values per call, so compare the
|
|
102
|
+
# key set: ALL + tokens must return the same unfiltered key set as ALL.
|
|
103
|
+
keys_all = set(json.loads(body)["getReadings"])
|
|
104
|
+
keys_mixed = set(json.loads(body_mixed)["getReadings"])
|
|
105
|
+
assert keys_mixed == keys_all, "ALL + tokens must not filter any keys"
|
|
106
|
+
print(f" OK: status={status_mixed} keys={len(keys_mixed)} (same as ALL)")
|
|
107
|
+
|
|
92
108
|
print()
|
|
93
109
|
print("=" * 60)
|
|
94
110
|
print("TEST 2: Raw HTTP - /getConfig without credentials -> 401")
|
|
@@ -14,6 +14,7 @@ from .const_api import (
|
|
|
14
14
|
API_GET_WEATHER_DATA,
|
|
15
15
|
API_PRIORITY_LOW,
|
|
16
16
|
API_READINGS,
|
|
17
|
+
QUERY_FULL_REFRESH,
|
|
17
18
|
)
|
|
18
19
|
from .readings import VioletReadings
|
|
19
20
|
|
|
@@ -81,6 +82,14 @@ class ReadingsMixin(APIClientMixin):
|
|
|
81
82
|
to work unchanged. Typed properties (``readings.pump``,
|
|
82
83
|
``readings.ph``, etc.) are available as an additive convenience.
|
|
83
84
|
|
|
85
|
+
The request uses ``ALL`` combined with every feature-flag token
|
|
86
|
+
(``DOSAGE``, ``RUNTIMES``, ``PUMPPRIOSTATE``, ``BACKWASH``,
|
|
87
|
+
``SYSTEM``): plain ``ALL`` omits the computed fields these tokens
|
|
88
|
+
unlock — daily dosing totals, runtime strings, priority-state
|
|
89
|
+
composites, backwash counters, SYSTEM diagnostics — which leaves the
|
|
90
|
+
corresponding sensors at ``unknown`` (firmware ``getReadings.js``,
|
|
91
|
+
observed after a controller restart).
|
|
92
|
+
|
|
84
93
|
Returns:
|
|
85
94
|
A :class:`VioletReadings` instance wrapping all readings.
|
|
86
95
|
|
|
@@ -90,7 +99,7 @@ class ReadingsMixin(APIClientMixin):
|
|
|
90
99
|
"""
|
|
91
100
|
response = await self._request_json_dict(
|
|
92
101
|
API_READINGS,
|
|
93
|
-
query=
|
|
102
|
+
query=QUERY_FULL_REFRESH,
|
|
94
103
|
payload_name="getReadings",
|
|
95
104
|
)
|
|
96
105
|
flat = self._flatten_getreadings_response(response)
|
|
@@ -109,7 +118,7 @@ class ReadingsMixin(APIClientMixin):
|
|
|
109
118
|
"""
|
|
110
119
|
response = await self._request_json_dict(
|
|
111
120
|
API_READINGS,
|
|
112
|
-
query=
|
|
121
|
+
query=QUERY_FULL_REFRESH,
|
|
113
122
|
payload_name="getReadings",
|
|
114
123
|
)
|
|
115
124
|
# Use flat dict directly (VioletReadings wrapping not needed here)
|
|
@@ -109,6 +109,11 @@ LOG_TYPES = frozenset({LOG_TYPE_ACTIONS, LOG_TYPE_SWITCHING, LOG_TYPE_ONEWIRE})
|
|
|
109
109
|
# Mixing ``ALL`` with such prefixes is fine but redundant.
|
|
110
110
|
#
|
|
111
111
|
# Source: includes/getReadings.js (firmware 1.0.9).
|
|
112
|
+
# Feature-flag tokens of the query language above. Plain ``ALL`` omits
|
|
113
|
+
# every field these tokens unlock, so a "give me everything" refresh must
|
|
114
|
+
# always combine ``ALL`` with all of them.
|
|
115
|
+
FEATURE_FLAG_TOKENS = ("DOSAGE", "RUNTIMES", "PUMPPRIOSTATE", "BACKWASH", "SYSTEM")
|
|
116
|
+
|
|
112
117
|
SPECIFIC_READING_GROUPS = (
|
|
113
118
|
"ADC",
|
|
114
119
|
"DOSAGE",
|
|
@@ -125,6 +130,9 @@ SPECIFIC_READING_GROUPS = (
|
|
|
125
130
|
)
|
|
126
131
|
SPECIFIC_FULL_REFRESH_INTERVAL = 10 # Number of updates before a full refresh
|
|
127
132
|
|
|
133
|
+
# Complete-dataset query: base READINGS plus every computed field group.
|
|
134
|
+
QUERY_FULL_REFRESH = ",".join(("ALL", *FEATURE_FLAG_TOKENS))
|
|
135
|
+
|
|
128
136
|
# =============================================================================
|
|
129
137
|
# API ACTIONS
|
|
130
138
|
# =============================================================================
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_api_smoke.py
RENAMED
|
File without changes
|
{violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_circuit_breaker.py
RENAMED
|
File without changes
|
{violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_language_policy.py
RENAMED
|
File without changes
|
|
File without changes
|
{violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_rate_limiter.py
RENAMED
|
File without changes
|
{violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_readings.py
RENAMED
|
File without changes
|
{violet_poolcontroller_api-0.0.39 → violet_poolcontroller_api-0.0.40}/tests/test_sanitizer.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
|