python-pooldose 0.6.0__tar.gz → 0.6.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.
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/PKG-INFO +106 -14
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/README.md +105 -13
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/__init__.py +1 -1
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/__main__.py +94 -2
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/client.py +33 -16
- python_pooldose-0.6.6/src/pooldose/device_analyzer.py +411 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/mappings/mapping_info.py +3 -3
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/mappings/model_PDPR1H1HAR1V0_FW539224.json +34 -26
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/mock_client.py +1 -1
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/request_handler.py +99 -18
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/values/instant_values.py +1 -1
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/python_pooldose.egg-info/PKG-INFO +106 -14
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/python_pooldose.egg-info/SOURCES.txt +3 -0
- python_pooldose-0.6.6/tests/test_device_analyzer.py +583 -0
- python_pooldose-0.6.6/tests/test_device_analyzer_integration.py +389 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/LICENSE +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/pyproject.toml +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/setup.cfg +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/constants.py +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/mappings/__init__.py +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/mappings/model_PDPR1H1HAW100_FW539187.json +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/py.typed +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/request_status.py +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/values/__init__.py +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/values/static_values.py +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/python_pooldose.egg-info/dependency_links.txt +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/python_pooldose.egg-info/entry_points.txt +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/python_pooldose.egg-info/requires.txt +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/python_pooldose.egg-info/top_level.txt +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/tests/test_client.py +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/tests/test_instant_values.py +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/tests/test_mapping_info.py +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/tests/test_request_handler.py +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.6}/tests/test_ssl_support.py +0 -0
- {python_pooldose-0.6.0 → python_pooldose-0.6.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.6.
|
|
3
|
+
Version: 0.6.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
|
|
@@ -28,6 +28,7 @@ This client uses an undocumented local HTTP API. It provides live readings for p
|
|
|
28
28
|
- **Dynamic sensor discovery** based on device model and firmware
|
|
29
29
|
- **Dictionary-style access** to instant values
|
|
30
30
|
- **Structured data API** with type-based organization
|
|
31
|
+
- **Device analyzer** for discovering unsupported device capabilities
|
|
31
32
|
- **PEP-561 compliant** with full type hints for Home Assistant integrations
|
|
32
33
|
- **Command-line interface** for direct device interaction and testing
|
|
33
34
|
- **Secure by default** - WiFi passwords excluded unless explicitly requested
|
|
@@ -186,6 +187,12 @@ pooldose --host 192.168.1.100 --ssl
|
|
|
186
187
|
|
|
187
188
|
# Custom port
|
|
188
189
|
pooldose --host 192.168.1.100 --ssl --port 8443
|
|
190
|
+
|
|
191
|
+
# Analyze device capabilities (discover unsupported devices)
|
|
192
|
+
pooldose --host 192.168.1.100 --analyze
|
|
193
|
+
|
|
194
|
+
# Show all widgets including hidden ones
|
|
195
|
+
pooldose --host 192.168.1.100 --analyze-all
|
|
189
196
|
```
|
|
190
197
|
|
|
191
198
|
### Mock Mode with JSON Files
|
|
@@ -203,6 +210,9 @@ You can also run it as a Python module:
|
|
|
203
210
|
# Real device
|
|
204
211
|
python -m pooldose --host 192.168.1.100
|
|
205
212
|
|
|
213
|
+
# Device analysis
|
|
214
|
+
python -m pooldose --host 192.168.1.100 --analyze
|
|
215
|
+
|
|
206
216
|
# Mock mode
|
|
207
217
|
python -m pooldose --mock data.json
|
|
208
218
|
|
|
@@ -210,6 +220,93 @@ python -m pooldose --mock data.json
|
|
|
210
220
|
python -m pooldose --help
|
|
211
221
|
```
|
|
212
222
|
|
|
223
|
+
## Device Analysis for Unsupported Devices
|
|
224
|
+
|
|
225
|
+
The device analyzer is a powerful feature that helps discover and analyze PoolDose devices that are not yet officially supported. This is particularly useful for:
|
|
226
|
+
|
|
227
|
+
- **New Device Discovery**: Identifying capabilities of unknown device models
|
|
228
|
+
- **Device Support Development**: Gathering data needed to add support for new devices
|
|
229
|
+
- **Troubleshooting**: Understanding how your device exposes data and controls
|
|
230
|
+
- **Widget Exploration**: Discovering all available sensors, controls, and settings
|
|
231
|
+
|
|
232
|
+
### Basic Device Analysis
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# Analyze a device to discover its capabilities
|
|
236
|
+
pooldose --host 192.168.1.100 --analyze
|
|
237
|
+
|
|
238
|
+
# Show all widgets including hidden ones
|
|
239
|
+
pooldose --host 192.168.1.100 --analyze-all
|
|
240
|
+
|
|
241
|
+
# Analyze with HTTPS
|
|
242
|
+
pooldose --host 192.168.1.100 --ssl --analyze
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Analysis Output
|
|
246
|
+
|
|
247
|
+
The analyzer provides comprehensive information about your device:
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
=== DEVICE ANALYSIS ===
|
|
251
|
+
Device: 01234567890A_DEVICE
|
|
252
|
+
Model: PDPR1H1HAW***
|
|
253
|
+
Firmware: FW53****
|
|
254
|
+
|
|
255
|
+
=== WIDGETS (Visible UI Elements) ===
|
|
256
|
+
|
|
257
|
+
SENSORS (Read-only values)
|
|
258
|
+
temperature: 24.5°C
|
|
259
|
+
ph: 7.2
|
|
260
|
+
orp: 720 mV
|
|
261
|
+
|
|
262
|
+
SETPOINTS (Configurable values)
|
|
263
|
+
target_ph: 7.0 (Range: 6.0-8.0, Step: 0.1)
|
|
264
|
+
target_orp: 700 mV (Range: 400-900, Step: 10)
|
|
265
|
+
|
|
266
|
+
SWITCHES (On/Off controls)
|
|
267
|
+
stop_dosing: OFF
|
|
268
|
+
pump_detection: ON
|
|
269
|
+
|
|
270
|
+
SELECTS (Configuration options)
|
|
271
|
+
water_meter_unit: L/h
|
|
272
|
+
Options: [L/h, m³/h, gal/h]
|
|
273
|
+
|
|
274
|
+
ALARMS (Status indicators)
|
|
275
|
+
alarm_ph: OK
|
|
276
|
+
alarm_orp: OK
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Using Analysis for Device Support
|
|
280
|
+
|
|
281
|
+
When you encounter an unsupported device, the analyzer helps gather the necessary information:
|
|
282
|
+
|
|
283
|
+
1. **Run Analysis**: Use `--analyze` to discover all device capabilities
|
|
284
|
+
2. **Document Output**: Save the analysis output to understand device structure
|
|
285
|
+
3. **Check Widget Types**: Note which sensors, controls, and settings are available
|
|
286
|
+
4. **Identify Patterns**: Look for device model and firmware information
|
|
287
|
+
5. **Report Findings**: Use the analysis data to request support for your device model
|
|
288
|
+
|
|
289
|
+
### Example: Discovering New Device
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# Unknown device analysis
|
|
293
|
+
pooldose --host 192.168.1.100 --analyze
|
|
294
|
+
|
|
295
|
+
# Output shows:
|
|
296
|
+
# Device: 01987654321B_DEVICE
|
|
297
|
+
# Model: PDPR2H2XYZ*** ← New model not yet supported
|
|
298
|
+
# Firmware: FW54**** ← New firmware version
|
|
299
|
+
#
|
|
300
|
+
# Widgets discovered: 15 sensors, 8 controls, 12 settings
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
With this information, you can:
|
|
304
|
+
- Report the new model/firmware combination
|
|
305
|
+
- Share the widget structure for mapping development
|
|
306
|
+
- Help expand device support for the community
|
|
307
|
+
|
|
308
|
+
The device analyzer makes python-pooldose extensible and helps build support for the growing ecosystem of SEKO PoolDose devices.
|
|
309
|
+
|
|
213
310
|
## Examples
|
|
214
311
|
|
|
215
312
|
The `examples/` directory contains demonstration scripts that show how to use the python-pooldose library:
|
|
@@ -638,13 +735,13 @@ Mapping Discovery Process:
|
|
|
638
735
|
│
|
|
639
736
|
▼
|
|
640
737
|
┌─────────────────┐
|
|
641
|
-
│ Get MODEL_ID │ ──────►
|
|
642
|
-
│ Get FW_CODE │ ──────►
|
|
738
|
+
│ Get MODEL_ID │ ──────► PDPR1H1HAW***
|
|
739
|
+
│ Get FW_CODE │ ──────► 53****
|
|
643
740
|
└─────────────────┘
|
|
644
741
|
│
|
|
645
742
|
▼
|
|
646
743
|
┌─────────────────┐
|
|
647
|
-
│ Load JSON File │ ──────►
|
|
744
|
+
│ Load JSON File │ ──────► model_PDPR1H1HAW***_FW53****.json
|
|
648
745
|
└─────────────────┘
|
|
649
746
|
│
|
|
650
747
|
▼
|
|
@@ -762,10 +859,10 @@ The `instant_values_structured()` method returns data organized by type:
|
|
|
762
859
|
|
|
763
860
|
This client has been tested with:
|
|
764
861
|
|
|
765
|
-
- **SEKO PoolDose Double/Dual WiFi** (Model:
|
|
766
|
-
- **VA dos BASIC Chlor - pH/ORP Wi-Fi** (Model:
|
|
862
|
+
- **SEKO PoolDose Double/Dual WiFi** (Model: PDPR1H1HAW***, FW: 53****)
|
|
863
|
+
- **VA dos BASIC Chlor - pH/ORP Wi-Fi** (Model: PDPR1H1HAR***, FW: 53****)
|
|
767
864
|
|
|
768
|
-
Other SEKO PoolDose models may work but are untested. The client uses JSON mapping files to adapt to different device models and firmware versions (see e.g. `src/pooldose/mappings/
|
|
865
|
+
Other SEKO PoolDose models may work but are untested. The client uses JSON mapping files to adapt to different device models and firmware versions (see e.g. `src/pooldose/mappings/model_PDPR1H1HAW***_FW53****.json`).
|
|
769
866
|
|
|
770
867
|
> **Note:** The JSON files in the mappings directory define the device-specific data keys and their human-readable names for different PoolDose models and firmware versions.
|
|
771
868
|
|
|
@@ -845,11 +942,6 @@ Data Classification:
|
|
|
845
942
|
|
|
846
943
|
For detailed release notes and version history, please see [CHANGELOG.md](CHANGELOG.md).
|
|
847
944
|
|
|
848
|
-
### Latest Release (0.6.
|
|
945
|
+
### Latest Release (0.6.6)
|
|
849
946
|
|
|
850
|
-
- **
|
|
851
|
-
- **Pip Installation**: Install as console script via `pip install python-pooldose`
|
|
852
|
-
- **PEP-561 Type Compliance**: Full typing support for Home Assistant integrations
|
|
853
|
-
- **Documentation Modernization**: Centralized CLI documentation
|
|
854
|
-
- **Code Quality**: Pylint 10.00/10 score with strict typing and enhanced error handling
|
|
855
|
-
- **Simplified Structure**: Removed deprecated demo scripts, integrated mock functionality into CLI
|
|
947
|
+
- **Mapping**: Fixed for PDPR1H1HAR1V0_FW539224**
|
|
@@ -10,6 +10,7 @@ This client uses an undocumented local HTTP API. It provides live readings for p
|
|
|
10
10
|
- **Dynamic sensor discovery** based on device model and firmware
|
|
11
11
|
- **Dictionary-style access** to instant values
|
|
12
12
|
- **Structured data API** with type-based organization
|
|
13
|
+
- **Device analyzer** for discovering unsupported device capabilities
|
|
13
14
|
- **PEP-561 compliant** with full type hints for Home Assistant integrations
|
|
14
15
|
- **Command-line interface** for direct device interaction and testing
|
|
15
16
|
- **Secure by default** - WiFi passwords excluded unless explicitly requested
|
|
@@ -168,6 +169,12 @@ pooldose --host 192.168.1.100 --ssl
|
|
|
168
169
|
|
|
169
170
|
# Custom port
|
|
170
171
|
pooldose --host 192.168.1.100 --ssl --port 8443
|
|
172
|
+
|
|
173
|
+
# Analyze device capabilities (discover unsupported devices)
|
|
174
|
+
pooldose --host 192.168.1.100 --analyze
|
|
175
|
+
|
|
176
|
+
# Show all widgets including hidden ones
|
|
177
|
+
pooldose --host 192.168.1.100 --analyze-all
|
|
171
178
|
```
|
|
172
179
|
|
|
173
180
|
### Mock Mode with JSON Files
|
|
@@ -185,6 +192,9 @@ You can also run it as a Python module:
|
|
|
185
192
|
# Real device
|
|
186
193
|
python -m pooldose --host 192.168.1.100
|
|
187
194
|
|
|
195
|
+
# Device analysis
|
|
196
|
+
python -m pooldose --host 192.168.1.100 --analyze
|
|
197
|
+
|
|
188
198
|
# Mock mode
|
|
189
199
|
python -m pooldose --mock data.json
|
|
190
200
|
|
|
@@ -192,6 +202,93 @@ python -m pooldose --mock data.json
|
|
|
192
202
|
python -m pooldose --help
|
|
193
203
|
```
|
|
194
204
|
|
|
205
|
+
## Device Analysis for Unsupported Devices
|
|
206
|
+
|
|
207
|
+
The device analyzer is a powerful feature that helps discover and analyze PoolDose devices that are not yet officially supported. This is particularly useful for:
|
|
208
|
+
|
|
209
|
+
- **New Device Discovery**: Identifying capabilities of unknown device models
|
|
210
|
+
- **Device Support Development**: Gathering data needed to add support for new devices
|
|
211
|
+
- **Troubleshooting**: Understanding how your device exposes data and controls
|
|
212
|
+
- **Widget Exploration**: Discovering all available sensors, controls, and settings
|
|
213
|
+
|
|
214
|
+
### Basic Device Analysis
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Analyze a device to discover its capabilities
|
|
218
|
+
pooldose --host 192.168.1.100 --analyze
|
|
219
|
+
|
|
220
|
+
# Show all widgets including hidden ones
|
|
221
|
+
pooldose --host 192.168.1.100 --analyze-all
|
|
222
|
+
|
|
223
|
+
# Analyze with HTTPS
|
|
224
|
+
pooldose --host 192.168.1.100 --ssl --analyze
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Analysis Output
|
|
228
|
+
|
|
229
|
+
The analyzer provides comprehensive information about your device:
|
|
230
|
+
|
|
231
|
+
```
|
|
232
|
+
=== DEVICE ANALYSIS ===
|
|
233
|
+
Device: 01234567890A_DEVICE
|
|
234
|
+
Model: PDPR1H1HAW***
|
|
235
|
+
Firmware: FW53****
|
|
236
|
+
|
|
237
|
+
=== WIDGETS (Visible UI Elements) ===
|
|
238
|
+
|
|
239
|
+
SENSORS (Read-only values)
|
|
240
|
+
temperature: 24.5°C
|
|
241
|
+
ph: 7.2
|
|
242
|
+
orp: 720 mV
|
|
243
|
+
|
|
244
|
+
SETPOINTS (Configurable values)
|
|
245
|
+
target_ph: 7.0 (Range: 6.0-8.0, Step: 0.1)
|
|
246
|
+
target_orp: 700 mV (Range: 400-900, Step: 10)
|
|
247
|
+
|
|
248
|
+
SWITCHES (On/Off controls)
|
|
249
|
+
stop_dosing: OFF
|
|
250
|
+
pump_detection: ON
|
|
251
|
+
|
|
252
|
+
SELECTS (Configuration options)
|
|
253
|
+
water_meter_unit: L/h
|
|
254
|
+
Options: [L/h, m³/h, gal/h]
|
|
255
|
+
|
|
256
|
+
ALARMS (Status indicators)
|
|
257
|
+
alarm_ph: OK
|
|
258
|
+
alarm_orp: OK
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Using Analysis for Device Support
|
|
262
|
+
|
|
263
|
+
When you encounter an unsupported device, the analyzer helps gather the necessary information:
|
|
264
|
+
|
|
265
|
+
1. **Run Analysis**: Use `--analyze` to discover all device capabilities
|
|
266
|
+
2. **Document Output**: Save the analysis output to understand device structure
|
|
267
|
+
3. **Check Widget Types**: Note which sensors, controls, and settings are available
|
|
268
|
+
4. **Identify Patterns**: Look for device model and firmware information
|
|
269
|
+
5. **Report Findings**: Use the analysis data to request support for your device model
|
|
270
|
+
|
|
271
|
+
### Example: Discovering New Device
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
# Unknown device analysis
|
|
275
|
+
pooldose --host 192.168.1.100 --analyze
|
|
276
|
+
|
|
277
|
+
# Output shows:
|
|
278
|
+
# Device: 01987654321B_DEVICE
|
|
279
|
+
# Model: PDPR2H2XYZ*** ← New model not yet supported
|
|
280
|
+
# Firmware: FW54**** ← New firmware version
|
|
281
|
+
#
|
|
282
|
+
# Widgets discovered: 15 sensors, 8 controls, 12 settings
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
With this information, you can:
|
|
286
|
+
- Report the new model/firmware combination
|
|
287
|
+
- Share the widget structure for mapping development
|
|
288
|
+
- Help expand device support for the community
|
|
289
|
+
|
|
290
|
+
The device analyzer makes python-pooldose extensible and helps build support for the growing ecosystem of SEKO PoolDose devices.
|
|
291
|
+
|
|
195
292
|
## Examples
|
|
196
293
|
|
|
197
294
|
The `examples/` directory contains demonstration scripts that show how to use the python-pooldose library:
|
|
@@ -620,13 +717,13 @@ Mapping Discovery Process:
|
|
|
620
717
|
│
|
|
621
718
|
▼
|
|
622
719
|
┌─────────────────┐
|
|
623
|
-
│ Get MODEL_ID │ ──────►
|
|
624
|
-
│ Get FW_CODE │ ──────►
|
|
720
|
+
│ Get MODEL_ID │ ──────► PDPR1H1HAW***
|
|
721
|
+
│ Get FW_CODE │ ──────► 53****
|
|
625
722
|
└─────────────────┘
|
|
626
723
|
│
|
|
627
724
|
▼
|
|
628
725
|
┌─────────────────┐
|
|
629
|
-
│ Load JSON File │ ──────►
|
|
726
|
+
│ Load JSON File │ ──────► model_PDPR1H1HAW***_FW53****.json
|
|
630
727
|
└─────────────────┘
|
|
631
728
|
│
|
|
632
729
|
▼
|
|
@@ -744,10 +841,10 @@ The `instant_values_structured()` method returns data organized by type:
|
|
|
744
841
|
|
|
745
842
|
This client has been tested with:
|
|
746
843
|
|
|
747
|
-
- **SEKO PoolDose Double/Dual WiFi** (Model:
|
|
748
|
-
- **VA dos BASIC Chlor - pH/ORP Wi-Fi** (Model:
|
|
844
|
+
- **SEKO PoolDose Double/Dual WiFi** (Model: PDPR1H1HAW***, FW: 53****)
|
|
845
|
+
- **VA dos BASIC Chlor - pH/ORP Wi-Fi** (Model: PDPR1H1HAR***, FW: 53****)
|
|
749
846
|
|
|
750
|
-
Other SEKO PoolDose models may work but are untested. The client uses JSON mapping files to adapt to different device models and firmware versions (see e.g. `src/pooldose/mappings/
|
|
847
|
+
Other SEKO PoolDose models may work but are untested. The client uses JSON mapping files to adapt to different device models and firmware versions (see e.g. `src/pooldose/mappings/model_PDPR1H1HAW***_FW53****.json`).
|
|
751
848
|
|
|
752
849
|
> **Note:** The JSON files in the mappings directory define the device-specific data keys and their human-readable names for different PoolDose models and firmware versions.
|
|
753
850
|
|
|
@@ -827,11 +924,6 @@ Data Classification:
|
|
|
827
924
|
|
|
828
925
|
For detailed release notes and version history, please see [CHANGELOG.md](CHANGELOG.md).
|
|
829
926
|
|
|
830
|
-
### Latest Release (0.6.
|
|
927
|
+
### Latest Release (0.6.6)
|
|
831
928
|
|
|
832
|
-
- **
|
|
833
|
-
- **Pip Installation**: Install as console script via `pip install python-pooldose`
|
|
834
|
-
- **PEP-561 Type Compliance**: Full typing support for Home Assistant integrations
|
|
835
|
-
- **Documentation Modernization**: Centralized CLI documentation
|
|
836
|
-
- **Code Quality**: Pylint 10.00/10 score with strict typing and enhanced error handling
|
|
837
|
-
- **Simplified Structure**: Removed deprecated demo scripts, integrated mock functionality into CLI
|
|
929
|
+
- **Mapping**: Fixed for PDPR1H1HAR1V0_FW539224**
|
|
@@ -8,7 +8,9 @@ from pathlib import Path
|
|
|
8
8
|
|
|
9
9
|
from pooldose import __version__
|
|
10
10
|
from pooldose.client import PooldoseClient, RequestStatus
|
|
11
|
+
from pooldose.device_analyzer import DeviceAnalyzer
|
|
11
12
|
from pooldose.mock_client import MockPooldoseClient
|
|
13
|
+
from pooldose.request_handler import RequestHandler
|
|
12
14
|
|
|
13
15
|
# Import demo utilities if available
|
|
14
16
|
try:
|
|
@@ -43,6 +45,66 @@ except ImportError:
|
|
|
43
45
|
print(f" {key}: {value}")
|
|
44
46
|
|
|
45
47
|
|
|
48
|
+
async def run_device_analyzer(host: str, use_ssl: bool, port: int, show_all: bool = False) -> None:
|
|
49
|
+
"""Run the DeviceAnalyzer for unknown devices."""
|
|
50
|
+
print(f"Analyzing unknown device at {host}")
|
|
51
|
+
if use_ssl:
|
|
52
|
+
print(f"Using HTTPS on port {port}")
|
|
53
|
+
else:
|
|
54
|
+
print(f"Using HTTP on port {port}")
|
|
55
|
+
|
|
56
|
+
if show_all:
|
|
57
|
+
print("Showing ALL widgets (including hidden ones)")
|
|
58
|
+
else:
|
|
59
|
+
print("Showing only VISIBLE widgets (use --analyze-all for all)")
|
|
60
|
+
|
|
61
|
+
# Create request handler
|
|
62
|
+
handler = RequestHandler(
|
|
63
|
+
host=host,
|
|
64
|
+
timeout=30,
|
|
65
|
+
use_ssl=use_ssl,
|
|
66
|
+
port=port if port != 0 else None,
|
|
67
|
+
ssl_verify=False
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
# Test connection
|
|
72
|
+
print("Testing connection...")
|
|
73
|
+
if not handler.check_host_reachable():
|
|
74
|
+
print("Host not reachable!")
|
|
75
|
+
return
|
|
76
|
+
print("Host is reachable")
|
|
77
|
+
|
|
78
|
+
# Connect and initialize
|
|
79
|
+
print("\nConnecting and initializing...")
|
|
80
|
+
status = await handler.connect()
|
|
81
|
+
if status != RequestStatus.SUCCESS:
|
|
82
|
+
print(f"Connection failed: {status}")
|
|
83
|
+
return
|
|
84
|
+
print("Connected successfully")
|
|
85
|
+
print(f" Software Version: {handler.software_version}")
|
|
86
|
+
print(f" API Version: {handler.api_version}")
|
|
87
|
+
|
|
88
|
+
# Create and run analyzer
|
|
89
|
+
analyzer = DeviceAnalyzer(handler)
|
|
90
|
+
device_info, widgets, analysis_status = await analyzer.analyze_device()
|
|
91
|
+
|
|
92
|
+
if analysis_status != RequestStatus.SUCCESS:
|
|
93
|
+
print(f"Analysis failed: {analysis_status}")
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
# Display results
|
|
97
|
+
if device_info is not None:
|
|
98
|
+
analyzer.display_analysis(device_info, widgets, show_all=show_all)
|
|
99
|
+
else:
|
|
100
|
+
print("Failed to analyze device - no device info available")
|
|
101
|
+
|
|
102
|
+
except (ConnectionError, TimeoutError, OSError) as e:
|
|
103
|
+
print(f"Network error: {e}")
|
|
104
|
+
except Exception as e: # pylint: disable=broad-except
|
|
105
|
+
print(f"Error during analysis: {e}")
|
|
106
|
+
|
|
107
|
+
|
|
46
108
|
async def run_real_client(host: str, use_ssl: bool, port: int) -> None:
|
|
47
109
|
"""Run the real PooldoseClient."""
|
|
48
110
|
print(f"Connecting to PoolDose device at {host}")
|
|
@@ -138,6 +200,12 @@ Examples:
|
|
|
138
200
|
# Connect with HTTPS
|
|
139
201
|
python -m pooldose --host 192.168.1.100 --ssl --port 443
|
|
140
202
|
|
|
203
|
+
# Analyze unknown device (visible widgets only)
|
|
204
|
+
python -m pooldose --host 192.168.1.100 --analyze
|
|
205
|
+
|
|
206
|
+
# Analyze unknown device (all widgets including hidden)
|
|
207
|
+
python -m pooldose --host 192.168.1.100 --analyze-all
|
|
208
|
+
|
|
141
209
|
# Use mock client with JSON file
|
|
142
210
|
python -m pooldose --mock path/to/your/data.json
|
|
143
211
|
""",
|
|
@@ -170,6 +238,16 @@ Examples:
|
|
|
170
238
|
default=0,
|
|
171
239
|
help="Custom port (default: 80 for HTTP, 443 for HTTPS)"
|
|
172
240
|
)
|
|
241
|
+
parser.add_argument(
|
|
242
|
+
"--analyze",
|
|
243
|
+
action="store_true",
|
|
244
|
+
help="Analyze unknown device (requires --host)"
|
|
245
|
+
)
|
|
246
|
+
parser.add_argument(
|
|
247
|
+
"--analyze-all",
|
|
248
|
+
action="store_true",
|
|
249
|
+
help="Analyze unknown device including hidden widgets (implies --analyze)"
|
|
250
|
+
)
|
|
173
251
|
parser.add_argument(
|
|
174
252
|
"--version",
|
|
175
253
|
action="version",
|
|
@@ -178,8 +256,16 @@ Examples:
|
|
|
178
256
|
|
|
179
257
|
args = parser.parse_args()
|
|
180
258
|
|
|
259
|
+
# Handle analyze-all implies analyze
|
|
260
|
+
if args.analyze_all:
|
|
261
|
+
args.analyze = True
|
|
262
|
+
|
|
263
|
+
# Validation: --analyze requires --host
|
|
264
|
+
if args.analyze and not args.host:
|
|
265
|
+
parser.error("--analyze requires --host to be specified")
|
|
266
|
+
|
|
181
267
|
# Set UTF-8 encoding for output
|
|
182
|
-
if sys.stdout.encoding != 'utf-8':
|
|
268
|
+
if hasattr(sys.stdout, 'reconfigure') and sys.stdout.encoding != 'utf-8':
|
|
183
269
|
sys.stdout.reconfigure(encoding='utf-8')
|
|
184
270
|
|
|
185
271
|
print("Python PoolDose Client")
|
|
@@ -189,7 +275,13 @@ Examples:
|
|
|
189
275
|
if args.host:
|
|
190
276
|
# Real device mode
|
|
191
277
|
port = args.port if args.port != 0 else (443 if args.ssl else 80)
|
|
192
|
-
|
|
278
|
+
if args.analyze:
|
|
279
|
+
# Device analysis mode
|
|
280
|
+
asyncio.run(run_device_analyzer(
|
|
281
|
+
args.host, args.ssl, port, show_all=args.analyze_all))
|
|
282
|
+
else:
|
|
283
|
+
# Normal client mode
|
|
284
|
+
asyncio.run(run_real_client(args.host, args.ssl, port))
|
|
193
285
|
elif args.mock:
|
|
194
286
|
# Mock mode
|
|
195
287
|
asyncio.run(run_mock_client(args.mock))
|
|
@@ -44,7 +44,7 @@ class PooldoseClient:
|
|
|
44
44
|
self._port = port
|
|
45
45
|
self._ssl_verify = ssl_verify
|
|
46
46
|
self._last_data = None
|
|
47
|
-
self._request_handler = None
|
|
47
|
+
self._request_handler: Optional[RequestHandler] = None
|
|
48
48
|
|
|
49
49
|
# Initialize device info with default or placeholder values
|
|
50
50
|
self.device_info = get_default_device_info()
|
|
@@ -57,7 +57,7 @@ class PooldoseClient:
|
|
|
57
57
|
async def connect(self) -> RequestStatus:
|
|
58
58
|
"""
|
|
59
59
|
Asynchronously connect to the device and initialize all components.
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
Returns:
|
|
62
62
|
RequestStatus: SUCCESS if connected successfully, otherwise appropriate error status.
|
|
63
63
|
"""
|
|
@@ -84,7 +84,14 @@ class PooldoseClient:
|
|
|
84
84
|
_LOGGER.debug("Initialized Pooldose client with device info: %s", self.device_info)
|
|
85
85
|
return RequestStatus.SUCCESS
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
@property
|
|
88
|
+
def request_handler(self) -> RequestHandler:
|
|
89
|
+
"""Get the request handler, ensuring it's initialized."""
|
|
90
|
+
if self._request_handler is None:
|
|
91
|
+
raise RuntimeError("Client not connected. Call connect() first.")
|
|
92
|
+
return self._request_handler
|
|
93
|
+
|
|
94
|
+
def check_apiversion_supported(self) -> Tuple[RequestStatus, Dict[str, Any]]:
|
|
88
95
|
"""
|
|
89
96
|
Check if the loaded API version matches the supported version.
|
|
90
97
|
|
|
@@ -92,11 +99,15 @@ class PooldoseClient:
|
|
|
92
99
|
tuple: (RequestStatus, dict)
|
|
93
100
|
- dict contains:
|
|
94
101
|
"api_version_is": the current API version (or None if not set)
|
|
95
|
-
"api_version_should": the
|
|
96
|
-
- RequestStatus.SUCCESS if supported,
|
|
97
|
-
- RequestStatus.API_VERSION_UNSUPPORTED if not supported,
|
|
102
|
+
"api_version_should": the expected API version
|
|
98
103
|
- RequestStatus.NO_DATA if not set.
|
|
99
104
|
"""
|
|
105
|
+
if self._request_handler is None:
|
|
106
|
+
return RequestStatus.NO_DATA, {
|
|
107
|
+
"api_version_is": None,
|
|
108
|
+
"api_version_should": API_VERSION_SUPPORTED,
|
|
109
|
+
}
|
|
110
|
+
|
|
100
111
|
result = {
|
|
101
112
|
"api_version_is": self._request_handler.api_version,
|
|
102
113
|
"api_version_should": API_VERSION_SUPPORTED,
|
|
@@ -110,7 +121,7 @@ class PooldoseClient:
|
|
|
110
121
|
|
|
111
122
|
return RequestStatus.SUCCESS, result
|
|
112
123
|
|
|
113
|
-
async def _load_device_info(self) -> RequestStatus:
|
|
124
|
+
async def _load_device_info(self) -> RequestStatus: # pylint: disable=too-many-branches
|
|
114
125
|
"""
|
|
115
126
|
Load device information from the request handler.
|
|
116
127
|
This method should be called after a successful connection.
|
|
@@ -139,10 +150,13 @@ class PooldoseClient:
|
|
|
139
150
|
await asyncio.sleep(0.5)
|
|
140
151
|
|
|
141
152
|
# Load mapping information
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
153
|
+
model_id = self.device_info.get("MODEL_ID")
|
|
154
|
+
fw_code = self.device_info.get("FW_CODE")
|
|
155
|
+
if model_id and fw_code:
|
|
156
|
+
self._mapping_info = await MappingInfo.load(str(model_id), str(fw_code))
|
|
157
|
+
else:
|
|
158
|
+
_LOGGER.warning("Missing MODEL_ID or FW_CODE, cannot load mapping")
|
|
159
|
+
self._mapping_info = MappingInfo(mapping=None, status=RequestStatus.NO_DATA)
|
|
146
160
|
|
|
147
161
|
# WiFi station info
|
|
148
162
|
status, wifi_station = await self._request_handler.get_wifi_station()
|
|
@@ -207,6 +221,9 @@ class PooldoseClient:
|
|
|
207
221
|
tuple: (RequestStatus, InstantValues|None) - Status and instant values object.
|
|
208
222
|
"""
|
|
209
223
|
try:
|
|
224
|
+
if self._request_handler is None:
|
|
225
|
+
return RequestStatus.NO_DATA, None
|
|
226
|
+
|
|
210
227
|
status, raw_data = await self._request_handler.get_values_raw()
|
|
211
228
|
if status != RequestStatus.SUCCESS or raw_data is None:
|
|
212
229
|
return status, None
|
|
@@ -214,10 +231,10 @@ class PooldoseClient:
|
|
|
214
231
|
mapping = self._mapping_info.mapping if self._mapping_info else None
|
|
215
232
|
if mapping is None:
|
|
216
233
|
return RequestStatus.UNKNOWN_ERROR, None
|
|
217
|
-
device_id = self.device_info
|
|
234
|
+
device_id = str(self.device_info.get("DEVICE_ID", ""))
|
|
218
235
|
device_raw_data = raw_data.get("devicedata", {}).get(device_id, {})
|
|
219
|
-
model_id = self.device_info
|
|
220
|
-
fw_code = self.device_info
|
|
236
|
+
model_id = str(self.device_info.get("MODEL_ID", ""))
|
|
237
|
+
fw_code = str(self.device_info.get("FW_CODE", ""))
|
|
221
238
|
prefix = f"{model_id}_FW{fw_code}_"
|
|
222
239
|
return RequestStatus.SUCCESS, InstantValues(device_raw_data, mapping, prefix, device_id, self._request_handler)
|
|
223
240
|
except (KeyError, TypeError, ValueError) as err:
|
|
@@ -227,13 +244,13 @@ class PooldoseClient:
|
|
|
227
244
|
async def instant_values_structured(self) -> Tuple[RequestStatus, Dict[str, Any]]:
|
|
228
245
|
"""
|
|
229
246
|
Get instant values in structured JSON format with types as top-level keys.
|
|
230
|
-
|
|
247
|
+
|
|
231
248
|
Returns:
|
|
232
249
|
Tuple[RequestStatus, Dict[str, Any]]: Status and structured data dict.
|
|
233
250
|
"""
|
|
234
251
|
# Get instant values object
|
|
235
252
|
status, instant_values = await self.instant_values()
|
|
236
|
-
if status != RequestStatus.SUCCESS:
|
|
253
|
+
if status != RequestStatus.SUCCESS or instant_values is None:
|
|
237
254
|
return status, {}
|
|
238
255
|
|
|
239
256
|
try:
|