python-pooldose 0.8.5__tar.gz → 0.8.6__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 (41) hide show
  1. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/PKG-INFO +7 -7
  2. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/README.md +6 -6
  3. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/__init__.py +1 -1
  4. python_pooldose-0.8.6/src/pooldose/mappings/model_PDPR1H04AW100_FW539292.json +330 -0
  5. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/mappings/model_PDPR1H1HAW100_FW539187.json +9 -9
  6. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/python_pooldose.egg-info/PKG-INFO +7 -7
  7. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/python_pooldose.egg-info/SOURCES.txt +1 -0
  8. python_pooldose-0.8.6/tests/test_mapping_info.py +194 -0
  9. python_pooldose-0.8.5/tests/test_mapping_info.py +0 -148
  10. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/LICENSE +0 -0
  11. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/pyproject.toml +0 -0
  12. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/setup.cfg +0 -0
  13. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/__main__.py +0 -0
  14. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/client.py +0 -0
  15. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/constants.py +0 -0
  16. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/device_analyzer.py +0 -0
  17. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/mappings/__init__.py +0 -0
  18. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/mappings/mapping_info.py +0 -0
  19. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/mappings/model_PDHC1H1HAR1V1_FW539224.json +0 -0
  20. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/mappings/model_PDPH1H1HAW100_FW539176.json +0 -0
  21. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/mappings/model_PDPR1H1HAR1V0_FW539224.json +0 -0
  22. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/mock_client.py +0 -0
  23. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/py.typed +0 -0
  24. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/request_handler.py +0 -0
  25. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/request_status.py +0 -0
  26. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/type_definitions.py +0 -0
  27. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/values/__init__.py +0 -0
  28. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/values/instant_values.py +0 -0
  29. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/pooldose/values/static_values.py +0 -0
  30. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/python_pooldose.egg-info/dependency_links.txt +0 -0
  31. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/python_pooldose.egg-info/entry_points.txt +0 -0
  32. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/python_pooldose.egg-info/requires.txt +0 -0
  33. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/src/python_pooldose.egg-info/top_level.txt +0 -0
  34. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/tests/test_client.py +0 -0
  35. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/tests/test_device_analyzer.py +0 -0
  36. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/tests/test_device_analyzer_integration.py +0 -0
  37. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/tests/test_instant_values.py +0 -0
  38. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/tests/test_mock_client_set_value.py +0 -0
  39. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/tests/test_request_handler.py +0 -0
  40. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/tests/test_ssl_support.py +0 -0
  41. {python_pooldose-0.8.5 → python_pooldose-0.8.6}/tests/test_static_values.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-pooldose
3
- Version: 0.8.5
3
+ Version: 0.8.6
4
4
  Summary: Unoffical async Python client for SEKO PoolDose devices
5
5
  Author-email: Lukas Maertin <pypi@lukas-maertin.de>
6
6
  License-Expression: MIT
@@ -904,7 +904,8 @@ This client has been tested with:
904
904
 
905
905
  | Device | PRODUCT_CODE | FW Code | Notes |
906
906
  |---|---|---|---|
907
- | SEKO PoolDose Double | PDPR1H1HAW100 | 539187 | Extended mapping (54 entities) |
907
+ | SEKO PoolDose Double | PDPR1H1HAW100 | 539187 | |
908
+ | SEKO PoolDose Double Spa | PDPR1H04AW100 | 539292 | |
908
909
  | SEKO POOLDOSE pH+ORP CF Group Wi-Fi | PDPR1H1HAW102 | 539187 | Alias for PDPR1H1HAW100 mapping |
909
910
  | SEKO PoolDose pH | PDPH1H1HAW100 | 539176 | pH-only device |
910
911
  | VÁGNER POOL VA DOS BASIC | PDHC1H1HAR1V0 | 539224 | |
@@ -990,9 +991,8 @@ Data Classification:
990
991
 
991
992
  For detailed release notes and version history, please see [CHANGELOG.md](CHANGELOG.md).
992
993
 
993
- ### Latest Release (0.8.5)
994
+ ### Latest Release (0.8.6)
994
995
 
995
- - Added support for **SEKO POOLDOSE pH+ORP CF Group Wi-Fi** (`PDPR1H1HAW102`) and **SEKO PoolDose pH** (`PDPH1H1HAW100`) devices
996
- - Extended `PDPR1H1HAW100` mapping from 32 to 54 entities
997
- - Improved session consistency in `RequestHandler`
998
- - Thanks to [@ronaldvdmeer](https://github.com/ronaldvdmeer) and [@pwntester](https://github.com/pwntester)
996
+ - Added support for **SEKO POOLDOSE Double SPA** (`PDPR1H04AW100`) device
997
+ - Changed sensors with Disable/Enable values to binary sensors (mapping values to binary states)
998
+ - Thanks to [@ronaldvdmeer](https://github.com/ronaldvdmeer) for contributing again
@@ -885,7 +885,8 @@ This client has been tested with:
885
885
 
886
886
  | Device | PRODUCT_CODE | FW Code | Notes |
887
887
  |---|---|---|---|
888
- | SEKO PoolDose Double | PDPR1H1HAW100 | 539187 | Extended mapping (54 entities) |
888
+ | SEKO PoolDose Double | PDPR1H1HAW100 | 539187 | |
889
+ | SEKO PoolDose Double Spa | PDPR1H04AW100 | 539292 | |
889
890
  | SEKO POOLDOSE pH+ORP CF Group Wi-Fi | PDPR1H1HAW102 | 539187 | Alias for PDPR1H1HAW100 mapping |
890
891
  | SEKO PoolDose pH | PDPH1H1HAW100 | 539176 | pH-only device |
891
892
  | VÁGNER POOL VA DOS BASIC | PDHC1H1HAR1V0 | 539224 | |
@@ -971,9 +972,8 @@ Data Classification:
971
972
 
972
973
  For detailed release notes and version history, please see [CHANGELOG.md](CHANGELOG.md).
973
974
 
974
- ### Latest Release (0.8.5)
975
+ ### Latest Release (0.8.6)
975
976
 
976
- - Added support for **SEKO POOLDOSE pH+ORP CF Group Wi-Fi** (`PDPR1H1HAW102`) and **SEKO PoolDose pH** (`PDPH1H1HAW100`) devices
977
- - Extended `PDPR1H1HAW100` mapping from 32 to 54 entities
978
- - Improved session consistency in `RequestHandler`
979
- - Thanks to [@ronaldvdmeer](https://github.com/ronaldvdmeer) and [@pwntester](https://github.com/pwntester)
977
+ - Added support for **SEKO POOLDOSE Double SPA** (`PDPR1H04AW100`) device
978
+ - Changed sensors with Disable/Enable values to binary sensors (mapping values to binary states)
979
+ - Thanks to [@ronaldvdmeer](https://github.com/ronaldvdmeer) for contributing again
@@ -1,5 +1,5 @@
1
1
  """Async API client for SEKO Pooldose."""
2
2
  from .client import PooldoseClient
3
3
 
4
- __version__ = "0.8.5"
4
+ __version__ = "0.8.6"
5
5
  __all__ = ["PooldoseClient"]
@@ -0,0 +1,330 @@
1
+ {
2
+ "temperature": {
3
+ "key": "w_1eommf39k",
4
+ "type": "sensor"
5
+ },
6
+ "ph": {
7
+ "key": "w_1ekeigkin",
8
+ "type": "sensor"
9
+ },
10
+ "orp": {
11
+ "key": "w_1eklenb23",
12
+ "type": "sensor"
13
+ },
14
+ "cl": {
15
+ "key": "w_1eo03t46k",
16
+ "type": "sensor"
17
+ },
18
+ "ph_type_dosing": {
19
+ "key": "w_1eklg44ro",
20
+ "type": "sensor",
21
+ "conversion": {
22
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklg44ro_ALCALYNE|": "alcalyne",
23
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklg44ro_ACID|": "acid"
24
+ }
25
+ },
26
+ "peristaltic_ph_dosing": {
27
+ "key": "w_1eklj6euj",
28
+ "type": "sensor",
29
+ "conversion": {
30
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklj6euj_OFF|": "off",
31
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklj6euj_PROPORTIONAL|": "proportional",
32
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklj6euj_ON_OFF|": "on / off",
33
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklj6euj_TIMED|": "timed",
34
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklj6euj_CYCLE|": "cycle"
35
+ }
36
+ },
37
+ "orp_type_dosing": {
38
+ "key": "w_1eklgnolb",
39
+ "type": "sensor",
40
+ "conversion": {
41
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklgnolb_LOW|": "low",
42
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklgnolb_HIGH|": "high"
43
+ }
44
+ },
45
+ "peristaltic_orp_dosing": {
46
+ "key": "w_1eo1s18s8",
47
+ "type": "sensor",
48
+ "conversion": {
49
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eo1s18s8_OFF|": "off",
50
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eo1s18s8_PROPORTIONAL|": "proportional",
51
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eo1s18s8_ON_OFF|": "on / off",
52
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eo1s18s8_TIMED|": "timed",
53
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eo1s18s8_CYCLE|": "cycle"
54
+ }
55
+ },
56
+ "peristaltic_cl_dosing": {
57
+ "key": "w_1eo1sf1m6",
58
+ "type": "sensor",
59
+ "conversion": {
60
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eo1sf1m6_OFF|": "off",
61
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eo1sf1m6_PROPORTIONAL|": "proportional",
62
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eo1sf1m6_ON_OFF|": "on / off",
63
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eo1sf1m6_TIMED|": "timed",
64
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eo1sf1m6_CYCLE|": "cycle"
65
+ }
66
+ },
67
+ "ph_calibration_type": {
68
+ "key": "w_1eklh8gb7",
69
+ "type": "sensor",
70
+ "conversion": {
71
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklh8gb7_OFF|": "off",
72
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklh8gb7_REFERENCE|": "reference",
73
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklh8gb7_1_POINT|": "1_point",
74
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklh8gb7_2_POINTS|": "2_points"
75
+ }
76
+ },
77
+ "ph_calibration_offset": {
78
+ "key": "w_1eklhs3b4",
79
+ "type": "sensor"
80
+ },
81
+ "ph_calibration_slope": {
82
+ "key": "w_1eklhs65u",
83
+ "type": "sensor"
84
+ },
85
+ "orp_calibration_type": {
86
+ "key": "w_1eklh8i5t",
87
+ "type": "sensor",
88
+ "conversion": {
89
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklh8i5t_OFF|": "off",
90
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklh8i5t_REFERENCE|": "reference",
91
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eklh8i5t_1_POINT|": "1_point"
92
+ }
93
+ },
94
+ "orp_calibration_offset": {
95
+ "key": "w_1eklhs8r3",
96
+ "type": "sensor"
97
+ },
98
+ "orp_calibration_slope": {
99
+ "key": "w_1eklhsase",
100
+ "type": "sensor"
101
+ },
102
+ "cl_calibration_type": {
103
+ "key": "w_1eo1umse8",
104
+ "type": "sensor",
105
+ "conversion": {
106
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eo1umse8_OFF|": "off",
107
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eo1umse8_2_POINT_CAL|": "2_point_cal"
108
+ }
109
+ },
110
+ "cl_calibration_offset": {
111
+ "key": "w_1eo1uno9u",
112
+ "type": "sensor"
113
+ },
114
+ "cl_calibration_slope": {
115
+ "key": "w_1eo1unpqb",
116
+ "type": "sensor"
117
+ },
118
+ "circulation_pump_status": {
119
+ "key": "w_1eo2fpohe",
120
+ "type": "binary_sensor",
121
+ "conversion": {
122
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eo2fpohe_DISABLED_|": false,
123
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eo2fpohe_ENABLED|": true
124
+ }
125
+ },
126
+ "ofa_ph_time": {
127
+ "key": "w_1eo1ttmft",
128
+ "type": "sensor"
129
+ },
130
+ "ofa_orp_time": {
131
+ "key": "w_1eo1tui1d",
132
+ "type": "sensor"
133
+ },
134
+ "ofa_cl_time": {
135
+ "key": "w_1eo1u082o",
136
+ "type": "sensor"
137
+ },
138
+ "pump_alarm": {
139
+ "key": "w_1ekga097n",
140
+ "type": "binary_sensor"
141
+ },
142
+ "ph_level_alarm": {
143
+ "key": "w_1eklf77pm",
144
+ "type": "binary_sensor"
145
+ },
146
+ "orp_level_alarm": {
147
+ "key": "w_1eo04bcr2",
148
+ "type": "binary_sensor"
149
+ },
150
+ "cl_level_alarm": {
151
+ "key": "w_1eo04dnhs",
152
+ "type": "binary_sensor"
153
+ },
154
+ "flow_rate_alarm": {
155
+ "key": "w_1eo04nc5n",
156
+ "type": "binary_sensor"
157
+ },
158
+ "relay_alarm": {
159
+ "key": "w_1eklffdl0",
160
+ "type": "binary_sensor"
161
+ },
162
+ "relay_aux1": {
163
+ "key": "w_1eoi2rv4h",
164
+ "type": "binary_sensor"
165
+ },
166
+ "relay_aux2": {
167
+ "key": "w_1eoi2s16b",
168
+ "type": "binary_sensor"
169
+ },
170
+ "alarm_ofa_ph": {
171
+ "key": "w_1eklfb73r",
172
+ "type": "binary_sensor"
173
+ },
174
+ "alarm_ofa2_ph": {
175
+ "key": "w_1eklfb8ob",
176
+ "type": "binary_sensor"
177
+ },
178
+ "alarm_ofa_orp": {
179
+ "key": "w_1eo1pabt6",
180
+ "type": "binary_sensor"
181
+ },
182
+ "alarm_ofa2_orp": {
183
+ "key": "w_1eo1paejq",
184
+ "type": "binary_sensor"
185
+ },
186
+ "alarm_ofa_cl": {
187
+ "key": "w_1eo1pju7i",
188
+ "type": "binary_sensor"
189
+ },
190
+ "alarm_ofa2_cl": {
191
+ "key": "w_1eo1pk0kl",
192
+ "type": "binary_sensor"
193
+ },
194
+ "alarm_water_too_cold": {
195
+ "key": "w_1fahufq7q",
196
+ "type": "binary_sensor"
197
+ },
198
+ "alarm_water_too_hot": {
199
+ "key": "w_1fahufrqe",
200
+ "type": "binary_sensor"
201
+ },
202
+ "alarm_ph_too_low": {
203
+ "key": "w_1fahuft7o",
204
+ "type": "binary_sensor"
205
+ },
206
+ "alarm_ph_too_high": {
207
+ "key": "w_1fahufum0",
208
+ "type": "binary_sensor"
209
+ },
210
+ "alarm_cl_too_low_orp": {
211
+ "key": "w_1fahug0lo",
212
+ "type": "binary_sensor"
213
+ },
214
+ "alarm_cl_too_high_orp": {
215
+ "key": "w_1fahug414",
216
+ "type": "binary_sensor"
217
+ },
218
+ "alarm_cl_too_low": {
219
+ "key": "w_1fahvmoal",
220
+ "type": "binary_sensor"
221
+ },
222
+ "alarm_cl_too_high": {
223
+ "key": "w_1fahvms2n",
224
+ "type": "binary_sensor"
225
+ },
226
+ "alarm_system_standby": {
227
+ "key": "w_1fai1n09b",
228
+ "type": "binary_sensor"
229
+ },
230
+ "ph_target": {
231
+ "key": "w_1ekeiqfat",
232
+ "type": "number"
233
+ },
234
+ "orp_target": {
235
+ "key": "w_1eklgnjk2",
236
+ "type": "number"
237
+ },
238
+ "cl_target": {
239
+ "key": "w_1eo1sejba",
240
+ "type": "number"
241
+ },
242
+ "time_on_ph_dosing": {
243
+ "key": "w_1eklj2r1a",
244
+ "type": "number"
245
+ },
246
+ "time_off_ph_dosing": {
247
+ "key": "w_1eklj30b7",
248
+ "type": "number"
249
+ },
250
+ "time_on_orp_dosing": {
251
+ "key": "w_1eo1v2uji",
252
+ "type": "number"
253
+ },
254
+ "time_off_orp_dosing": {
255
+ "key": "w_1eo1v30n4",
256
+ "type": "number"
257
+ },
258
+ "time_on_cl_dosing": {
259
+ "key": "w_1eo20i95c",
260
+ "type": "number"
261
+ },
262
+ "time_off_cl_dosing": {
263
+ "key": "w_1eo20ibtm",
264
+ "type": "number"
265
+ },
266
+ "power_on_delay_timer": {
267
+ "key": "w_1ffng5qhk",
268
+ "type": "number"
269
+ },
270
+ "flow_delay_timer": {
271
+ "key": "w_1ffng5spg",
272
+ "type": "number"
273
+ },
274
+ "pause_dosing": {
275
+ "key": "w_1emtltkel",
276
+ "type": "switch"
277
+ },
278
+ "pump_monitoring": {
279
+ "key": "w_1eklft47q",
280
+ "type": "switch"
281
+ },
282
+ "frequency_input": {
283
+ "key": "w_1eklft5qt",
284
+ "type": "switch"
285
+ },
286
+ "water_meter_unit": {
287
+ "key": "w_1eklinki6",
288
+ "type": "select",
289
+ "options": {
290
+ "0": "PDPR1H04AW100_FW539292_COMBO_w_1eklinki6_M_",
291
+ "1": "PDPR1H04AW100_FW539292_COMBO_w_1eklinki6_LITER"
292
+ },
293
+ "conversion": {
294
+ "PDPR1H04AW100_FW539292_COMBO_w_1eklinki6_M_": "m3",
295
+ "PDPR1H04AW100_FW539292_COMBO_w_1eklinki6_LITER": "L"
296
+ }
297
+ },
298
+ "device_config": {
299
+ "key": "w_1eu60qapu",
300
+ "type": "sensor",
301
+ "conversion": {
302
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eu60qapu_PH_ORP|": "ph_orp",
303
+ "|PDPR1H04AW100_FW539292_LABEL_w_1eu60qapu_PH_ORP_CHLORINE|": "ph_orp_chlorine"
304
+ }
305
+ },
306
+ "temperature_unit": {
307
+ "key": "w_1fkjtkd4g",
308
+ "type": "sensor",
309
+ "conversion": {
310
+ "|PDPR1H04AW100_FW539292_LABEL_w_1fkjtkd4g__C|": "celsius",
311
+ "|PDPR1H04AW100_FW539292_LABEL_w_1fkjtkd4g__F|": "fahrenheit"
312
+ }
313
+ },
314
+ "power_on_delay_status": {
315
+ "key": "w_1fhb0hqu2",
316
+ "type": "binary_sensor",
317
+ "conversion": {
318
+ "|PDPR1H04AW100_FW539292_LABEL_w_1fhb0hqu2_DISABLE|": false,
319
+ "|PDPR1H04AW100_FW539292_LABEL_w_1fhb0hqu2_ENABLE|": true
320
+ }
321
+ },
322
+ "flow_delay_status": {
323
+ "key": "w_1fhb0ht7t",
324
+ "type": "binary_sensor",
325
+ "conversion": {
326
+ "|PDPR1H04AW100_FW539292_LABEL_w_1fhb0ht7t_DISABLE|": false,
327
+ "|PDPR1H04AW100_FW539292_LABEL_w_1fhb0ht7t_ENABLE|": true
328
+ }
329
+ }
330
+ }
@@ -212,10 +212,10 @@
212
212
  },
213
213
  "circulation_pump_status": {
214
214
  "key": "w_1eo2fpohe",
215
- "type": "sensor",
215
+ "type": "binary_sensor",
216
216
  "conversion": {
217
- "|PDPR1H1HAW100_FW539187_LABEL_w_1eo2fpohe_DISABLED_|": "disabled",
218
- "|PDPR1H1HAW100_FW539187_LABEL_w_1eo2fpohe_ENABLED|": "enabled"
217
+ "|PDPR1H1HAW100_FW539187_LABEL_w_1eo2fpohe_DISABLED_|": false,
218
+ "|PDPR1H1HAW100_FW539187_LABEL_w_1eo2fpohe_ENABLED|": true
219
219
  }
220
220
  },
221
221
  "peristaltic_cl_dosing": {
@@ -247,18 +247,18 @@
247
247
  },
248
248
  "power_on_delay_status": {
249
249
  "key": "w_1fhb0hqu2",
250
- "type": "sensor",
250
+ "type": "binary_sensor",
251
251
  "conversion": {
252
- "|PDPR1H1HAW100_FW539187_LABEL_w_1fhb0hqu2_DISABLE|": "disabled",
253
- "|PDPR1H1HAW100_FW539187_LABEL_w_1fhb0hqu2_ENABLE|": "enabled"
252
+ "|PDPR1H1HAW100_FW539187_LABEL_w_1fhb0hqu2_DISABLE|": false,
253
+ "|PDPR1H1HAW100_FW539187_LABEL_w_1fhb0hqu2_ENABLE|": true
254
254
  }
255
255
  },
256
256
  "flow_delay_status": {
257
257
  "key": "w_1fhb0ht7t",
258
- "type": "sensor",
258
+ "type": "binary_sensor",
259
259
  "conversion": {
260
- "|PDPR1H1HAW100_FW539187_LABEL_w_1fhb0ht7t_DISABLE|": "disabled",
261
- "|PDPR1H1HAW100_FW539187_LABEL_w_1fhb0ht7t_ENABLE|": "enabled"
260
+ "|PDPR1H1HAW100_FW539187_LABEL_w_1fhb0ht7t_DISABLE|": false,
261
+ "|PDPR1H1HAW100_FW539187_LABEL_w_1fhb0ht7t_ENABLE|": true
262
262
  }
263
263
  },
264
264
  "time_off_ph_dosing": {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-pooldose
3
- Version: 0.8.5
3
+ Version: 0.8.6
4
4
  Summary: Unoffical async Python client for SEKO PoolDose devices
5
5
  Author-email: Lukas Maertin <pypi@lukas-maertin.de>
6
6
  License-Expression: MIT
@@ -904,7 +904,8 @@ This client has been tested with:
904
904
 
905
905
  | Device | PRODUCT_CODE | FW Code | Notes |
906
906
  |---|---|---|---|
907
- | SEKO PoolDose Double | PDPR1H1HAW100 | 539187 | Extended mapping (54 entities) |
907
+ | SEKO PoolDose Double | PDPR1H1HAW100 | 539187 | |
908
+ | SEKO PoolDose Double Spa | PDPR1H04AW100 | 539292 | |
908
909
  | SEKO POOLDOSE pH+ORP CF Group Wi-Fi | PDPR1H1HAW102 | 539187 | Alias for PDPR1H1HAW100 mapping |
909
910
  | SEKO PoolDose pH | PDPH1H1HAW100 | 539176 | pH-only device |
910
911
  | VÁGNER POOL VA DOS BASIC | PDHC1H1HAR1V0 | 539224 | |
@@ -990,9 +991,8 @@ Data Classification:
990
991
 
991
992
  For detailed release notes and version history, please see [CHANGELOG.md](CHANGELOG.md).
992
993
 
993
- ### Latest Release (0.8.5)
994
+ ### Latest Release (0.8.6)
994
995
 
995
- - Added support for **SEKO POOLDOSE pH+ORP CF Group Wi-Fi** (`PDPR1H1HAW102`) and **SEKO PoolDose pH** (`PDPH1H1HAW100`) devices
996
- - Extended `PDPR1H1HAW100` mapping from 32 to 54 entities
997
- - Improved session consistency in `RequestHandler`
998
- - Thanks to [@ronaldvdmeer](https://github.com/ronaldvdmeer) and [@pwntester](https://github.com/pwntester)
996
+ - Added support for **SEKO POOLDOSE Double SPA** (`PDPR1H04AW100`) device
997
+ - Changed sensors with Disable/Enable values to binary sensors (mapping values to binary states)
998
+ - Thanks to [@ronaldvdmeer](https://github.com/ronaldvdmeer) for contributing again
@@ -15,6 +15,7 @@ src/pooldose/mappings/__init__.py
15
15
  src/pooldose/mappings/mapping_info.py
16
16
  src/pooldose/mappings/model_PDHC1H1HAR1V1_FW539224.json
17
17
  src/pooldose/mappings/model_PDPH1H1HAW100_FW539176.json
18
+ src/pooldose/mappings/model_PDPR1H04AW100_FW539292.json
18
19
  src/pooldose/mappings/model_PDPR1H1HAR1V0_FW539224.json
19
20
  src/pooldose/mappings/model_PDPR1H1HAW100_FW539187.json
20
21
  src/pooldose/values/__init__.py
@@ -0,0 +1,194 @@
1
+ """Tests for MappingInfo for async API client for SEKO Pooldose."""
2
+
3
+ import pytest
4
+ from pooldose.mappings.mapping_info import MappingInfo, SensorMapping, SelectMapping
5
+ from pooldose.request_handler import RequestStatus
6
+
7
+ async def test_load_file_not_found():
8
+ """Test MappingInfo.load returns UNKNOWN_ERROR if file not found."""
9
+ mapping_info = await MappingInfo.load("DOESNOTEXIST", "000000")
10
+ assert mapping_info.status != RequestStatus.SUCCESS
11
+ assert mapping_info.mapping is None
12
+
13
+ def test_available_types_and_sensors():
14
+ """Test available_types and available_sensors return correct structure."""
15
+ # Prepare fake mapping
16
+ fake_mapping = {
17
+ "temp_actual": {"key": "k1", "type": "sensor"},
18
+ "ph_actual": {"key": "k2", "type": "sensor", "conversion": {"a": "b"}},
19
+ "sel1": {"key": "k3", "type": "select", "conversion": {"x": "y"}, "options": {"o": 1}},
20
+ }
21
+ mapping_info = MappingInfo(mapping=fake_mapping, status=RequestStatus.SUCCESS)
22
+
23
+ types = mapping_info.available_types()
24
+ assert "sensor" in types
25
+ assert "select" in types
26
+ assert "temp_actual" in types["sensor"]
27
+ assert "ph_actual" in types["sensor"]
28
+ assert "sel1" in types["select"]
29
+
30
+ sensors = mapping_info.available_sensors()
31
+ assert "temp_actual" in sensors
32
+ assert isinstance(sensors["temp_actual"], SensorMapping)
33
+ assert sensors["temp_actual"].key == "k1"
34
+ assert sensors["temp_actual"].conversion is None
35
+
36
+ assert "ph_actual" in sensors
37
+ assert sensors["ph_actual"].conversion == {"a": "b"}
38
+
39
+ def test_available_selects():
40
+ """Test available_selects returns correct SelectMapping objects."""
41
+ fake_mapping = {
42
+ "sel1": {"key": "k3", "type": "select", "conversion": {"x": "y"}, "options": {"o": 1}},
43
+ }
44
+ mapping_info = MappingInfo(mapping=fake_mapping, status=RequestStatus.SUCCESS)
45
+ selects = mapping_info.available_selects()
46
+ assert "sel1" in selects
47
+ select = selects["sel1"]
48
+ assert isinstance(select, SelectMapping)
49
+ assert select.key == "k3"
50
+ assert select.conversion == {"x": "y"}
51
+ assert select.options == {"o": 1}
52
+
53
+ def test_available_selects_missing_fields():
54
+ """Test available_selects raises KeyError if conversion/options missing."""
55
+ fake_mapping = {
56
+ "sel1": {"key": "k3", "type": "select", "conversion": {"x": "y"}}, # missing options
57
+ "sel2": {"key": "k4", "type": "select", "options": {"o": 1}}, # missing conversion
58
+ }
59
+ mapping_info = MappingInfo(mapping=fake_mapping, status=RequestStatus.SUCCESS)
60
+ with pytest.raises(KeyError):
61
+ _ = mapping_info.available_selects()
62
+
63
+
64
+ class TestDoubleSpaMapping:
65
+ """Tests for the POOLDOSE DOUBLE SPA mapping file (PDPR1H04AW100_FW539292)."""
66
+
67
+ @pytest.mark.asyncio
68
+ async def test_load_double_spa_mapping(self):
69
+ """Test that the DOUBLE SPA mapping file loads successfully."""
70
+ mapping_info = await MappingInfo.load("PDPR1H04AW100", "539292")
71
+ assert mapping_info.status == RequestStatus.SUCCESS
72
+ assert mapping_info.mapping is not None
73
+
74
+ @pytest.mark.asyncio
75
+ async def test_double_spa_sensors(self):
76
+ """Test that expected sensors are present in the DOUBLE SPA mapping."""
77
+ mapping_info = await MappingInfo.load("PDPR1H04AW100", "539292")
78
+ sensors = mapping_info.available_sensors()
79
+
80
+ # Core measurement sensors
81
+ expected_sensors = [
82
+ "temperature", "ph", "orp", "cl",
83
+ "ph_type_dosing", "peristaltic_ph_dosing",
84
+ "orp_type_dosing", "peristaltic_orp_dosing",
85
+ "peristaltic_cl_dosing",
86
+ "ph_calibration_type", "ph_calibration_offset", "ph_calibration_slope",
87
+ "orp_calibration_type", "orp_calibration_offset", "orp_calibration_slope",
88
+ "cl_calibration_type", "cl_calibration_offset", "cl_calibration_slope",
89
+ "ofa_ph_time", "ofa_orp_time", "ofa_cl_time",
90
+ "device_config", "temperature_unit",
91
+ ]
92
+ for name in expected_sensors:
93
+ assert name in sensors, f"Missing sensor: {name}"
94
+
95
+ @pytest.mark.asyncio
96
+ async def test_double_spa_sensor_conversions(self):
97
+ """Test that sensors with conversions have the correct label prefix."""
98
+ mapping_info = await MappingInfo.load("PDPR1H04AW100", "539292")
99
+ sensors = mapping_info.available_sensors()
100
+
101
+ # pH type dosing should have conversion with PDPR1H04AW100_FW539292 prefix
102
+ ph_type = sensors["ph_type_dosing"]
103
+ assert ph_type.conversion is not None
104
+ assert "|PDPR1H04AW100_FW539292_LABEL_w_1eklg44ro_ACID|" in ph_type.conversion
105
+ assert ph_type.conversion["|PDPR1H04AW100_FW539292_LABEL_w_1eklg44ro_ACID|"] == "acid"
106
+
107
+ # Peristaltic CL dosing - unique to DOUBLE SPA
108
+ cl_dosing = sensors["peristaltic_cl_dosing"]
109
+ assert cl_dosing.conversion is not None
110
+ assert len(cl_dosing.conversion) == 5 # off, proportional, on/off, timed, cycle
111
+
112
+ @pytest.mark.asyncio
113
+ async def test_double_spa_binary_sensors(self):
114
+ """Test that expected binary sensors are present, including DOUBLE SPA extras."""
115
+ mapping_info = await MappingInfo.load("PDPR1H04AW100", "539292")
116
+ binary_sensors = mapping_info.available_binary_sensors()
117
+
118
+ # Standard alarms (shared with PDPR1H1HAW100)
119
+ standard_alarms = [
120
+ "pump_alarm", "ph_level_alarm", "orp_level_alarm",
121
+ "flow_rate_alarm", "relay_alarm", "relay_aux1", "relay_aux2",
122
+ "alarm_ofa_ph", "alarm_ofa_orp",
123
+ ]
124
+ for name in standard_alarms:
125
+ assert name in binary_sensors, f"Missing binary_sensor: {name}"
126
+
127
+ # DOUBLE SPA-specific alarms
128
+ double_spa_alarms = [
129
+ "cl_level_alarm",
130
+ "alarm_ofa2_ph", "alarm_ofa2_orp",
131
+ "alarm_ofa_cl", "alarm_ofa2_cl",
132
+ "alarm_water_too_cold", "alarm_water_too_hot",
133
+ "alarm_ph_too_low", "alarm_ph_too_high",
134
+ "alarm_cl_too_low_orp", "alarm_cl_too_high_orp",
135
+ "alarm_cl_too_low", "alarm_cl_too_high",
136
+ "alarm_system_standby",
137
+ "circulation_pump_status",
138
+ "power_on_delay_status",
139
+ "flow_delay_status",
140
+ ]
141
+ for name in double_spa_alarms:
142
+ assert name in binary_sensors, f"Missing DOUBLE SPA binary_sensor: {name}"
143
+
144
+ @pytest.mark.asyncio
145
+ async def test_double_spa_numbers(self):
146
+ """Test that expected number entities are present."""
147
+ mapping_info = await MappingInfo.load("PDPR1H04AW100", "539292")
148
+ numbers = mapping_info.available_numbers()
149
+
150
+ expected_numbers = [
151
+ "ph_target", "orp_target", "cl_target",
152
+ "time_on_ph_dosing", "time_off_ph_dosing",
153
+ "time_on_orp_dosing", "time_off_orp_dosing",
154
+ "time_on_cl_dosing", "time_off_cl_dosing",
155
+ "power_on_delay_timer", "flow_delay_timer",
156
+ ]
157
+ for name in expected_numbers:
158
+ assert name in numbers, f"Missing number: {name}"
159
+
160
+ @pytest.mark.asyncio
161
+ async def test_double_spa_switches(self):
162
+ """Test that expected switch entities are present."""
163
+ mapping_info = await MappingInfo.load("PDPR1H04AW100", "539292")
164
+ switches = mapping_info.available_switches()
165
+
166
+ expected_switches = ["pause_dosing", "pump_monitoring", "frequency_input"]
167
+ for name in expected_switches:
168
+ assert name in switches, f"Missing switch: {name}"
169
+
170
+ @pytest.mark.asyncio
171
+ async def test_double_spa_selects(self):
172
+ """Test that select entities are present with correct options."""
173
+ mapping_info = await MappingInfo.load("PDPR1H04AW100", "539292")
174
+ selects = mapping_info.available_selects()
175
+
176
+ assert "water_meter_unit" in selects
177
+ wmu = selects["water_meter_unit"]
178
+ assert wmu.key == "w_1eklinki6"
179
+ assert "0" in wmu.options
180
+ assert "1" in wmu.options
181
+ assert "PDPR1H04AW100_FW539292_COMBO_w_1eklinki6_M_" in wmu.conversion
182
+ assert wmu.conversion["PDPR1H04AW100_FW539292_COMBO_w_1eklinki6_M_"] == "m3"
183
+
184
+ @pytest.mark.asyncio
185
+ async def test_double_spa_entity_counts(self):
186
+ """Test the total entity counts for the DOUBLE SPA mapping."""
187
+ mapping_info = await MappingInfo.load("PDPR1H04AW100", "539292")
188
+ types = mapping_info.available_types()
189
+
190
+ assert len(types.get("sensor", [])) == 23
191
+ assert len(types.get("binary_sensor", [])) == 26
192
+ assert len(types.get("number", [])) == 11
193
+ assert len(types.get("switch", [])) == 3
194
+ assert len(types.get("select", [])) == 1
@@ -1,148 +0,0 @@
1
- """Tests for MappingInfo for async API client for SEKO Pooldose."""
2
-
3
- import pytest
4
- from pooldose.mappings.mapping_info import MappingInfo, SensorMapping, SelectMapping
5
- from pooldose.request_handler import RequestStatus
6
-
7
- async def test_load_file_not_found():
8
- """Test MappingInfo.load returns UNKNOWN_ERROR if file not found."""
9
- mapping_info = await MappingInfo.load("DOESNOTEXIST", "000000")
10
- assert mapping_info.status != RequestStatus.SUCCESS
11
- assert mapping_info.mapping is None
12
-
13
- def test_available_types_and_sensors():
14
- """Test available_types and available_sensors return correct structure."""
15
- # Prepare fake mapping
16
- fake_mapping = {
17
- "temp_actual": {"key": "k1", "type": "sensor"},
18
- "ph_actual": {"key": "k2", "type": "sensor", "conversion": {"a": "b"}},
19
- "sel1": {"key": "k3", "type": "select", "conversion": {"x": "y"}, "options": {"o": 1}},
20
- }
21
- mapping_info = MappingInfo(mapping=fake_mapping, status=RequestStatus.SUCCESS)
22
-
23
- types = mapping_info.available_types()
24
- assert "sensor" in types
25
- assert "select" in types
26
- assert "temp_actual" in types["sensor"]
27
- assert "ph_actual" in types["sensor"]
28
- assert "sel1" in types["select"]
29
-
30
- sensors = mapping_info.available_sensors()
31
- assert "temp_actual" in sensors
32
- assert isinstance(sensors["temp_actual"], SensorMapping)
33
- assert sensors["temp_actual"].key == "k1"
34
- assert sensors["temp_actual"].conversion is None
35
-
36
- assert "ph_actual" in sensors
37
- assert sensors["ph_actual"].conversion == {"a": "b"}
38
-
39
- def test_available_selects():
40
- """Test available_selects returns correct SelectMapping objects."""
41
- fake_mapping = {
42
- "sel1": {"key": "k3", "type": "select", "conversion": {"x": "y"}, "options": {"o": 1}},
43
- }
44
- mapping_info = MappingInfo(mapping=fake_mapping, status=RequestStatus.SUCCESS)
45
- selects = mapping_info.available_selects()
46
- assert "sel1" in selects
47
- select = selects["sel1"]
48
- assert isinstance(select, SelectMapping)
49
- assert select.key == "k3"
50
- assert select.conversion == {"x": "y"}
51
- assert select.options == {"o": 1}
52
-
53
- def test_available_selects_missing_fields():
54
- """Test available_selects raises KeyError if conversion/options missing."""
55
- fake_mapping = {
56
- "sel1": {"key": "k3", "type": "select", "conversion": {"x": "y"}}, # missing options
57
- "sel2": {"key": "k4", "type": "select", "options": {"o": 1}}, # missing conversion
58
- }
59
- mapping_info = MappingInfo(mapping=fake_mapping, status=RequestStatus.SUCCESS)
60
- with pytest.raises(KeyError):
61
- _ = mapping_info.available_selects()
62
-
63
-
64
- class TestPDPR1H1HAW100FW539187Mapping:
65
- """Tests for the PDPR1H1HAW100 FW539187 mapping file."""
66
-
67
- MODEL_ID = "PDPR1H1HAW100"
68
- FW_CODE = "539187"
69
-
70
- @pytest.fixture
71
- async def mapping(self):
72
- """Load the mapping file."""
73
- info = await MappingInfo.load(self.MODEL_ID, self.FW_CODE)
74
- assert info.status == RequestStatus.SUCCESS
75
- assert info.mapping is not None
76
- return info
77
-
78
- async def test_load_success(self, mapping):
79
- """Test mapping file loads successfully."""
80
- assert mapping.status == RequestStatus.SUCCESS
81
-
82
- async def test_total_entity_count(self, mapping):
83
- """Test total number of mapped entities."""
84
- assert len(mapping.mapping) == 54
85
-
86
- async def test_entity_type_counts(self, mapping):
87
- """Test entity count per type."""
88
- types = mapping.available_types()
89
- assert len(types.get("sensor", [])) == 22
90
- assert len(types.get("binary_sensor", [])) == 20
91
- assert len(types.get("number", [])) == 8
92
- assert len(types.get("switch", [])) == 3
93
- assert len(types.get("select", [])) == 1
94
-
95
- async def test_new_alarm_binary_sensors(self, mapping):
96
- """Test new alarm binary sensors are present."""
97
- types = mapping.available_types()
98
- expected_alarms = [
99
- "alarm_ofa2_ph",
100
- "alarm_ofa2_orp",
101
- "alarm_ofa2_cl",
102
- "alarm_water_too_cold",
103
- "alarm_water_too_hot",
104
- "alarm_ph_too_low",
105
- "alarm_ph_too_high",
106
- "alarm_cl_too_low_orp",
107
- "alarm_cl_too_high_orp",
108
- "alarm_cl_too_high",
109
- "alarm_system_standby",
110
- ]
111
- for alarm in expected_alarms:
112
- assert alarm in types["binary_sensor"], f"Missing binary_sensor: {alarm}"
113
-
114
- async def test_new_sensors_with_conversions(self, mapping):
115
- """Test new sensors with conversion dictionaries."""
116
- sensors = mapping.available_sensors()
117
- sensors_with_conversion = [
118
- "circulation_pump_status",
119
- "peristaltic_cl_dosing",
120
- "device_config",
121
- "temperature_unit",
122
- "power_on_delay_status",
123
- "flow_delay_status",
124
- ]
125
- for name in sensors_with_conversion:
126
- assert name in sensors, f"Missing sensor: {name}"
127
- assert sensors[name].conversion is not None, f"Missing conversion for {name}"
128
- assert len(sensors[name].conversion) >= 2, f"Conversion too small for {name}"
129
-
130
- async def test_new_number_entities(self, mapping):
131
- """Test new number entities are present."""
132
- types = mapping.available_types()
133
- expected_numbers = [
134
- "time_off_ph_dosing",
135
- "time_off_orp_dosing",
136
- "time_off_cl_dosing",
137
- "power_on_delay_timer",
138
- "flow_delay_timer",
139
- ]
140
- for number in expected_numbers:
141
- assert number in types["number"], f"Missing number: {number}"
142
-
143
- async def test_select_entity(self, mapping):
144
- """Test select entity with options and conversion."""
145
- selects = mapping.available_selects()
146
- assert "water_meter_unit" in selects
147
- assert len(selects["water_meter_unit"].options) == 2
148
- assert len(selects["water_meter_unit"].conversion) == 2
File without changes