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.
Files changed (35) hide show
  1. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/PKG-INFO +106 -14
  2. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/README.md +105 -13
  3. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/__init__.py +1 -1
  4. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/__main__.py +94 -2
  5. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/client.py +33 -16
  6. python_pooldose-0.6.6/src/pooldose/device_analyzer.py +411 -0
  7. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/mappings/mapping_info.py +3 -3
  8. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/mappings/model_PDPR1H1HAR1V0_FW539224.json +34 -26
  9. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/mock_client.py +1 -1
  10. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/request_handler.py +99 -18
  11. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/values/instant_values.py +1 -1
  12. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/python_pooldose.egg-info/PKG-INFO +106 -14
  13. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/python_pooldose.egg-info/SOURCES.txt +3 -0
  14. python_pooldose-0.6.6/tests/test_device_analyzer.py +583 -0
  15. python_pooldose-0.6.6/tests/test_device_analyzer_integration.py +389 -0
  16. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/LICENSE +0 -0
  17. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/pyproject.toml +0 -0
  18. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/setup.cfg +0 -0
  19. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/constants.py +0 -0
  20. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/mappings/__init__.py +0 -0
  21. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/mappings/model_PDPR1H1HAW100_FW539187.json +0 -0
  22. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/py.typed +0 -0
  23. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/request_status.py +0 -0
  24. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/values/__init__.py +0 -0
  25. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/pooldose/values/static_values.py +0 -0
  26. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/python_pooldose.egg-info/dependency_links.txt +0 -0
  27. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/python_pooldose.egg-info/entry_points.txt +0 -0
  28. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/python_pooldose.egg-info/requires.txt +0 -0
  29. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/src/python_pooldose.egg-info/top_level.txt +0 -0
  30. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/tests/test_client.py +0 -0
  31. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/tests/test_instant_values.py +0 -0
  32. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/tests/test_mapping_info.py +0 -0
  33. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/tests/test_request_handler.py +0 -0
  34. {python_pooldose-0.6.0 → python_pooldose-0.6.6}/tests/test_ssl_support.py +0 -0
  35. {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.0
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 │ ──────► PDPR1H1HAW100
642
- │ Get FW_CODE │ ──────► 539187
738
+ │ Get MODEL_ID │ ──────► PDPR1H1HAW***
739
+ │ Get FW_CODE │ ──────► 53****
643
740
  └─────────────────┘
644
741
 
645
742
 
646
743
  ┌─────────────────┐
647
- │ Load JSON File │ ──────► model_PDPR1H1HAW100_FW539187.json
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: PDPR1H1HAW100, FW: 539187)
766
- - **VA dos BASIC Chlor - pH/ORP Wi-Fi** (Model: PDPR1H1HAR1V0, FW: 539224)
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/model_PDPR1H1HAW100_FW539187.json`).
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.0)
945
+ ### Latest Release (0.6.6)
849
946
 
850
- - **Command Line Interface**: Complete CLI with `--host`, `--mock`, `--ssl`, and `--port` options
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 │ ──────► PDPR1H1HAW100
624
- │ Get FW_CODE │ ──────► 539187
720
+ │ Get MODEL_ID │ ──────► PDPR1H1HAW***
721
+ │ Get FW_CODE │ ──────► 53****
625
722
  └─────────────────┘
626
723
 
627
724
 
628
725
  ┌─────────────────┐
629
- │ Load JSON File │ ──────► model_PDPR1H1HAW100_FW539187.json
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: PDPR1H1HAW100, FW: 539187)
748
- - **VA dos BASIC Chlor - pH/ORP Wi-Fi** (Model: PDPR1H1HAR1V0, FW: 539224)
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/model_PDPR1H1HAW100_FW539187.json`).
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.0)
927
+ ### Latest Release (0.6.6)
831
928
 
832
- - **Command Line Interface**: Complete CLI with `--host`, `--mock`, `--ssl`, and `--port` options
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**
@@ -1,5 +1,5 @@
1
1
  """Async API client for SEKO Pooldose."""
2
2
  from .client import PooldoseClient
3
3
 
4
- __version__ = "0.6.0"
4
+ __version__ = "0.6.6"
5
5
  __all__ = ["PooldoseClient"]
@@ -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
- asyncio.run(run_real_client(args.host, args.ssl, port))
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
- def check_apiversion_supported(self) -> tuple[RequestStatus, dict]:
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 supported API version
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
- self._mapping_info = await MappingInfo.load(
143
- self.device_info.get("MODEL_ID"),
144
- self.device_info.get("FW_CODE")
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["DEVICE_ID"]
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["MODEL_ID"]
220
- fw_code = self.device_info["FW_CODE"]
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: