onvif-python 0.0.8__tar.gz → 0.1.0__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 (121) hide show
  1. {onvif_python-0.0.8/onvif_python.egg-info → onvif_python-0.1.0}/PKG-INFO +105 -37
  2. {onvif_python-0.0.8 → onvif_python-0.1.0}/README.md +104 -33
  3. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/client.py +167 -79
  4. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/operator.py +3 -3
  5. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/uplink.py +1 -1
  6. onvif_python-0.1.0/onvif/utils/wsdl.py +350 -0
  7. {onvif_python-0.0.8 → onvif_python-0.1.0/onvif_python.egg-info}/PKG-INFO +105 -37
  8. {onvif_python-0.0.8 → onvif_python-0.1.0}/pyproject.toml +1 -4
  9. onvif_python-0.0.8/onvif/utils/wsdl.py +0 -297
  10. {onvif_python-0.0.8 → onvif_python-0.1.0}/LICENSE.md +0 -0
  11. {onvif_python-0.0.8 → onvif_python-0.1.0}/MANIFEST.in +0 -0
  12. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/__init__.py +0 -0
  13. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/__init__.py +0 -0
  14. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/accesscontrol.py +0 -0
  15. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/accessrules.py +0 -0
  16. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/actionengine.py +0 -0
  17. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/analytics/analytics.py +0 -0
  18. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/analytics/ruleengine.py +0 -0
  19. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/analyticsdevice.py +0 -0
  20. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/appmgmt.py +0 -0
  21. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/authenticationbehavior.py +0 -0
  22. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/credential.py +0 -0
  23. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/deviceio.py +0 -0
  24. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/devicemgmt.py +0 -0
  25. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/display.py +0 -0
  26. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/doorcontrol.py +0 -0
  27. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/events/events.py +0 -0
  28. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/events/notification.py +0 -0
  29. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/events/pullpoint.py +0 -0
  30. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/events/subscription.py +0 -0
  31. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/imaging.py +0 -0
  32. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/media.py +0 -0
  33. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/media2.py +0 -0
  34. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/provisioning.py +0 -0
  35. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/ptz.py +0 -0
  36. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/receiver.py +0 -0
  37. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/recording.py +0 -0
  38. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/replay.py +0 -0
  39. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/schedule.py +0 -0
  40. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/search.py +0 -0
  41. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/security/advancedsecurity.py +0 -0
  42. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/security/authorizationserver.py +0 -0
  43. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/security/dot1x.py +0 -0
  44. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/security/jwt.py +0 -0
  45. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/security/keystore.py +0 -0
  46. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/security/mediasigning.py +0 -0
  47. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/security/tlsserver.py +0 -0
  48. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/services/thermal.py +0 -0
  49. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/utils/__init__.py +0 -0
  50. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/utils/error_handlers.py +0 -0
  51. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/utils/exceptions.py +0 -0
  52. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/utils/xml_capture.py +0 -0
  53. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/utils/zeep.py +0 -0
  54. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
  55. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
  56. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
  57. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
  58. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
  59. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
  60. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
  61. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
  62. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
  63. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
  64. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
  65. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
  66. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
  67. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
  68. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
  69. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/display/display.wsdl +0 -0
  70. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
  71. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/display.wsdl +0 -0
  72. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
  73. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
  74. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
  75. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
  76. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
  77. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
  78. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
  79. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
  80. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
  81. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
  82. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
  83. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/receiver.wsdl +0 -0
  84. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/recording.wsdl +0 -0
  85. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/replay.wsdl +0 -0
  86. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
  87. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
  88. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/schema/common.xsd +0 -0
  89. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
  90. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
  91. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/search.wsdl +0 -0
  92. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
  93. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
  94. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
  95. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
  96. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
  97. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
  98. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
  99. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/index.html +0 -0
  100. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
  101. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
  102. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
  103. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
  104. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
  105. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
  106. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
  107. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
  108. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
  109. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
  110. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
  111. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
  112. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif_python.egg-info/SOURCES.txt +0 -0
  113. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif_python.egg-info/dependency_links.txt +0 -0
  114. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif_python.egg-info/requires.txt +0 -0
  115. {onvif_python-0.0.8 → onvif_python-0.1.0}/onvif_python.egg-info/top_level.txt +0 -0
  116. {onvif_python-0.0.8 → onvif_python-0.1.0}/setup.cfg +0 -0
  117. {onvif_python-0.0.8 → onvif_python-0.1.0}/setup.py +0 -0
  118. {onvif_python-0.0.8 → onvif_python-0.1.0}/tests/test_client.py +0 -0
  119. {onvif_python-0.0.8 → onvif_python-0.1.0}/tests/test_core.py +0 -0
  120. {onvif_python-0.0.8 → onvif_python-0.1.0}/tests/test_exceptions.py +0 -0
  121. {onvif_python-0.0.8 → onvif_python-0.1.0}/tests/test_wsdl_map.py +0 -0
@@ -1,14 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onvif-python
3
- Version: 0.0.8
3
+ Version: 0.1.0
4
4
  Summary: A comprehensive and developer-friendly Python library for working with ONVIF-compliant devices
5
5
  Author-email: Nirsimetri Technologies® <open@nirsimetri.com>
6
6
  Maintainer: Kaburagi
7
7
  License-Expression: MIT
8
- Project-URL: Homepage, https://github.com/nirsimetri/onvif-python
9
- Project-URL: Issues, https://github.com/nirsimetri/onvif-python/issues
10
8
  Project-URL: Documentation, https://deepwiki.com/nirsimetri/onvif-python
11
- Project-URL: Repository, https://github.com/nirsimetri/onvif-python.git
12
9
  Project-URL: Changelog, https://github.com/nirsimetri/onvif-python/releases
13
10
  Keywords: onvif,Camera,onvif-python,ip-camera,cctv,soap,surveillance,cctv cameras,soap-client,onvif-client,onvif-library,onvif-specifications
14
11
  Classifier: Development Status :: 5 - Production/Stable
@@ -43,9 +40,9 @@ Dynamic: license-file
43
40
 
44
41
  [![License](https://img.shields.io/badge/License-MIT-blue)](https://github.com/nirsimetri/onvif-python?tab=MIT-1-ov-file)
45
42
  [![DeepWiki](https://img.shields.io/badge/DeepWiki-AI%20Wiki-orange)](https://deepwiki.com/nirsimetri/onvif-python)
46
- [![Release](https://img.shields.io/badge/Release-v0.0.8-red?logo=archive)](https://github.com/nirsimetri/onvif-python/releases)
43
+ [![Release](https://img.shields.io/badge/Release-v0.1.0-red?logo=archive)](https://github.com/nirsimetri/onvif-python/releases)
47
44
  <br>
48
- [![PyPI](https://img.shields.io/badge/PyPI-0.0.8-yellow?logo=archive)](https://pypi.org/project/onvif-python/)
45
+ [![PyPI](https://img.shields.io/badge/PyPI-0.1.0-yellow?logo=archive)](https://pypi.org/project/onvif-python/)
49
46
  [![Downloads](https://img.shields.io/pypi/dm/onvif-python?label=PyPI%20Downloads)](https://clickpy.clickhouse.com/dashboard/onvif-python)
50
47
 
51
48
  **This project provides a comprehensive and developer-friendly Python library for working with ONVIF-compliant devices.** It is designed to be reliable, easy to integrate, and flexible enough to support a wide range of ONVIF profiles and services.
@@ -96,7 +93,14 @@ Create an instance of `ONVIFClient` by providing your device's IP address, port,
96
93
  ```python
97
94
  from onvif import ONVIFClient
98
95
 
96
+ # Basic connection
99
97
  client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
98
+
99
+ # With custom WSDL directory (optional)
100
+ client = ONVIFClient(
101
+ "192.168.1.17", 8000, "admin", "admin123",
102
+ wsdl_dir="/path/to/custom/wsdl" # Use custom WSDL files in this path
103
+ )
100
104
  ```
101
105
 
102
106
  **2. Create Service Instance**
@@ -189,8 +193,9 @@ The `ONVIFClient` class provides various configuration options to customize the
189
193
 
190
194
  | Parameter | Type | Required | Default | Description |
191
195
  |-----------|------|----------|---------|-------------|
192
- | `apply_patch` | `bool` | ❌ No | `True` | Enable zeep patching for better `xsd:any` field parsing and automatic flattening |
193
- | `capture_xml` | `bool` | ❌ No | `False` | Enable XML capture plugin for debugging SOAP requests/responses |
196
+ | `apply_patch` | `bool` | ❌ No | `True` | Enable zeep patching for better `xsd:any` field parsing and automatic flattening, applied at (>= [`v0.0.4`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.0.4)) |
197
+ | `capture_xml` | `bool` | ❌ No | `False` | Enable XML capture plugin for debugging SOAP requests/responses, applied at (>= [`v0.0.6`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.0.6)) |
198
+ | `wsdl_dir` | `str` | ❌ No | `None` | Custom WSDL directory path for using external WSDL files instead of built-in ones (e.g., `/path/to/custom/wsdl`), applied at (>= [`v0.1.0`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.1.0)) |
194
199
 
195
200
  </details>
196
201
 
@@ -320,6 +325,33 @@ if client.xml_plugin:
320
325
  > - `save_to_file(request_file, response_file)` - Save XML to files
321
326
  > - `clear_history()` - Clear captured history
322
327
 
328
+ **Custom WSDL Directory:**
329
+ ```python
330
+ from onvif import ONVIFClient
331
+
332
+ # Use custom WSDL files instead of built-in ones
333
+ client = ONVIFClient(
334
+ "192.168.1.17",
335
+ 80,
336
+ "admin",
337
+ "password",
338
+ wsdl_dir="/path/to/custom/wsdl" # Custom WSDL directory
339
+ )
340
+
341
+ # All services will automatically use custom WSDL files
342
+ device = client.devicemgmt()
343
+ media = client.media()
344
+ ptz = client.ptz()
345
+
346
+ # The custom WSDL directory should have a flat structure:
347
+ # /path/to/custom/wsdl/
348
+ # ├── devicemgmt.wsdl
349
+ # ├── media.wsdl
350
+ # ├── ptz.wsdl
351
+ # ├── imaging.wsdl
352
+ # └── ... (other WSDL files)
353
+ ```
354
+
323
355
  </details>
324
356
 
325
357
  <details>
@@ -339,7 +371,8 @@ client = ONVIFClient(
339
371
  use_https=True, # Secure communication
340
372
  verify_ssl=True, # Verify certificates (default)
341
373
  apply_patch=True, # Enhanced parsing (default)
342
- capture_xml=False # Disable debug mode (default)
374
+ capture_xml=False, # Disable debug mode (default)
375
+ wsdl_dir=None # Use built-in WSDL files (default)
343
376
  )
344
377
  ```
345
378
  </details>
@@ -349,12 +382,13 @@ client = ONVIFClient(
349
382
  - **Authentication:** This library uses **WS-UsernameToken with Digest** authentication by default, which is the standard for ONVIF devices.
350
383
  - **Patching:** The `apply_patch=True` (default) enables custom zeep patching that improves `xsd:any` field parsing. This is recommended for better compatibility with ONVIF responses.
351
384
  - **XML Capture:** Only use `capture_xml=True` during development/debugging as it increases memory usage and may expose sensitive data in logs.
385
+ - **Custom WSDL:** Use `wsdl_dir` parameter to specify a custom directory containing WSDL files. The directory should have a flat structure with WSDL files directly in the root (e.g., `/path/to/custom/wsdl/devicemgmt.wsdl`, `/path/to/custom/wsdl/media.wsdl`, etc.).
352
386
  - **Cache Location:** Disk cache (when using `CacheMode.DB` or `CacheMode.ALL`) is stored in `~/.onvif-python/onvif_zeep_cache.sqlite`.
353
387
 
354
388
  ## Service Discovery: Understanding Device Capabilities
355
389
 
356
390
  > [!WARNING]
357
- > Before performing any operations on an ONVIF device, it is highly recommended to discover which services are available and supported by the device. This library automatically uses `GetServices` during initialization to discover service endpoints, but you can also query services manually for detailed information including capabilities.
391
+ > Before performing any operations on an ONVIF device, it is highly recommended to discover which services are available and supported by the device. This library automatically performs comprehensive service discovery during initialization using a robust fallback mechanism.
358
392
 
359
393
  **Why discover device services?**
360
394
 
@@ -365,50 +399,84 @@ client = ONVIFClient(
365
399
 
366
400
  **How service discovery works in this library:**
367
401
 
368
- The `ONVIFClient` automatically calls `GetServices` during initialization to build a service map. This map is used internally to resolve service endpoints:
402
+ The `ONVIFClient` uses a **3-tier discovery approach** to maximize device compatibility:
403
+
404
+ 1. **GetServices (Preferred)** - Tries `GetServices` first for detailed service information
405
+ 2. **GetCapabilities (Fallback)** - Falls back to `GetCapabilities` if `GetServices` is not supported
406
+ 3. **Default URLs (Final Fallback)** - Uses standard ONVIF URLs as last resort
369
407
 
370
408
  ```python
371
409
  from onvif import ONVIFClient
372
410
 
373
411
  client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
374
412
 
375
- # Access the discovered services
376
- print(client.services)
377
- # Example output: [{'Namespace': 'http://www.onvif.org/ver10/device/wsdl', 'XAddr': '...', 'Version': {...}}, ...]
378
-
379
- # Check the service map (namespace -> XAddr mapping)
380
- print(client._service_map)
381
- # Example output: {'http://www.onvif.org/ver10/media/wsdl': 'http://192.168.1.17:8000/onvif/Media', ...}
413
+ # Check what discovery method was used
414
+ if client.services:
415
+ print("Service discovery: GetServices (preferred)")
416
+ print("Discovered services:", len(client.services))
417
+ print("Service map:", client._service_map)
418
+ elif client.capabilities:
419
+ print("Service discovery: GetCapabilities (fallback)")
420
+ print("Available capabilities:", client.capabilities)
421
+ else:
422
+ print("Service discovery: Using default URLs")
382
423
  ```
383
424
 
384
- **Get detailed service information with capabilities:**
425
+ **Why this approach?**
385
426
 
386
- If you need detailed capability information for each service, call `GetServices` with `IncludeCapability=True`:
427
+ - **GetServices** provides the most accurate and detailed service information, but it's **optional** in the ONVIF specification
428
+ - **GetCapabilities** is **mandatory** for all ONVIF-compliant devices, ensuring broader compatibility
429
+ - **Default URLs** guarantee basic connectivity even with non-compliant devices
430
+
431
+ **Get detailed service information:**
432
+
433
+ If you need comprehensive service details, you can manually call `GetServices` with capabilities:
387
434
 
388
435
  ```python
389
436
  device = client.devicemgmt()
390
- services = device.GetServices(IncludeCapability=True)
391
-
392
- for service in services:
393
- print(f"Service: {service.Namespace}")
394
- print(f"Endpoint: {service.XAddr}")
395
- print(f"Version: {service.Version.Major}.{service.Version.Minor}")
396
- if hasattr(service, 'Capabilities') and service.Capabilities:
397
- print(f"Capabilities: {service.Capabilities}")
437
+
438
+ try:
439
+ # Try to get detailed service information
440
+ services = device.GetServices(IncludeCapability=True)
441
+ for service in services:
442
+ print(f"Service: {service.Namespace}")
443
+ print(f"XAddr: {service.XAddr}")
444
+ if hasattr(service, 'Capabilities'):
445
+ print(f"Capabilities: {service.Capabilities}")
446
+ except Exception:
447
+ # Fallback to GetCapabilities for legacy devices
448
+ capabilities = device.GetCapabilities()
449
+ print("Capabilities:", capabilities)
398
450
  ```
399
451
 
400
- **Alternative: Use GetCapabilities for legacy compatibility:**
452
+ **Access capabilities information:**
401
453
 
402
- For backward compatibility or when you need a quick overview of major service categories, you can still use `GetCapabilities`:
454
+ When using `GetCapabilities` fallback, you can access capability information:
403
455
 
404
456
  ```python
405
- capabilities = client.devicemgmt().GetCapabilities()
406
- print(capabilities)
407
- # Example output: {'Media': {'XAddr': '...', ...}, 'PTZ': {...}, 'Events': {...}, ...}
457
+ device = client.devicemgmt()
458
+
459
+ try:
460
+ # Get capabilities information from device
461
+ capabilities = device.GetCapabilities()
462
+
463
+ # Main services (always available)
464
+ print("Media XAddr:", getattr(capabilities.Media, 'XAddr', 'Not available'))
465
+ print("PTZ XAddr:", getattr(capabilities.PTZ, 'XAddr', 'Not available'))
466
+
467
+ # Extension services (device-dependent)
468
+ ext = getattr(capabilities, 'Extension', None)
469
+ if ext:
470
+ print("DeviceIO XAddr:", getattr(ext.DeviceIO, 'XAddr', 'Not available'))
471
+ print("Recording XAddr:", getattr(ext.Recording, 'XAddr', 'Not available'))
472
+ print("Search XAddr:", getattr(ext.Search, 'XAddr', 'Not available'))
473
+ print("Replay XAddr:", getattr(ext.Replay, 'XAddr', 'Not available'))
474
+ except Exception as e:
475
+ print(f"Error getting capabilities: {e}")
408
476
  ```
409
477
 
410
478
  > [!TIP]
411
- > The library handles service discovery automatically, so you typically don't need to call `GetServices` manually unless you need detailed capability information or want to refresh the service list after device configuration changes.
479
+ > The library handles service discovery automatically with intelligent fallback. You typically don't need to call discovery methods manually unless you need detailed capability information or want to refresh the service list after device configuration changes.
412
480
 
413
481
  ## Tested Devices
414
482
 
@@ -561,10 +629,10 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
561
629
 
562
630
  ## Future Improvements (Stay tuned and star ⭐ this repo)
563
631
 
564
- - [x] Add debugging mode with raw xml on SOAP requests and responses. ([c258162](https://github.com/nirsimetri/onvif-python/commit/c258162))
565
- - [ ] Add functionality for `ONVIFClient` to accept a custom `wsdl_path` service.
566
- - [ ] Add asynchronous (async/await) support for non-blocking ONVIF operations and concurrent device communication.
632
+ - [x] ~~Add debugging mode with raw xml on SOAP requests and responses.~~ ([c258162](https://github.com/nirsimetri/onvif-python/commit/c258162))
633
+ - [x] ~~Add functionality for `ONVIFClient` to accept a custom `wsdl_dir` service.~~ ([65f2570](https://github.com/nirsimetri/onvif-python/commit/65f257092e4c9daa23dd0d00825ed38a45d23b70))
567
634
  - [ ] Add `ONVIF CLI` program to interact directly with ONVIF devices via terminal.
635
+ - [ ] Add asynchronous (async/await) support for non-blocking ONVIF operations and concurrent device communication.
568
636
  - [ ] Implement structured data models for ONVIF Schemas using [xsdata](https://github.com/tefra/xsdata).
569
637
  - [ ] Integrate [xmltodict](https://github.com/martinblech/xmltodict) for simplified XML parsing and conversion.
570
638
  - [ ] Enhance documentation with API references and diagrams (not from [AI Wiki](https://deepwiki.com/nirsimetri/onvif-python)).
@@ -2,9 +2,9 @@
2
2
 
3
3
  [![License](https://img.shields.io/badge/License-MIT-blue)](https://github.com/nirsimetri/onvif-python?tab=MIT-1-ov-file)
4
4
  [![DeepWiki](https://img.shields.io/badge/DeepWiki-AI%20Wiki-orange)](https://deepwiki.com/nirsimetri/onvif-python)
5
- [![Release](https://img.shields.io/badge/Release-v0.0.8-red?logo=archive)](https://github.com/nirsimetri/onvif-python/releases)
5
+ [![Release](https://img.shields.io/badge/Release-v0.1.0-red?logo=archive)](https://github.com/nirsimetri/onvif-python/releases)
6
6
  <br>
7
- [![PyPI](https://img.shields.io/badge/PyPI-0.0.8-yellow?logo=archive)](https://pypi.org/project/onvif-python/)
7
+ [![PyPI](https://img.shields.io/badge/PyPI-0.1.0-yellow?logo=archive)](https://pypi.org/project/onvif-python/)
8
8
  [![Downloads](https://img.shields.io/pypi/dm/onvif-python?label=PyPI%20Downloads)](https://clickpy.clickhouse.com/dashboard/onvif-python)
9
9
 
10
10
  **This project provides a comprehensive and developer-friendly Python library for working with ONVIF-compliant devices.** It is designed to be reliable, easy to integrate, and flexible enough to support a wide range of ONVIF profiles and services.
@@ -55,7 +55,14 @@ Create an instance of `ONVIFClient` by providing your device's IP address, port,
55
55
  ```python
56
56
  from onvif import ONVIFClient
57
57
 
58
+ # Basic connection
58
59
  client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
60
+
61
+ # With custom WSDL directory (optional)
62
+ client = ONVIFClient(
63
+ "192.168.1.17", 8000, "admin", "admin123",
64
+ wsdl_dir="/path/to/custom/wsdl" # Use custom WSDL files in this path
65
+ )
59
66
  ```
60
67
 
61
68
  **2. Create Service Instance**
@@ -148,8 +155,9 @@ The `ONVIFClient` class provides various configuration options to customize the
148
155
 
149
156
  | Parameter | Type | Required | Default | Description |
150
157
  |-----------|------|----------|---------|-------------|
151
- | `apply_patch` | `bool` | ❌ No | `True` | Enable zeep patching for better `xsd:any` field parsing and automatic flattening |
152
- | `capture_xml` | `bool` | ❌ No | `False` | Enable XML capture plugin for debugging SOAP requests/responses |
158
+ | `apply_patch` | `bool` | ❌ No | `True` | Enable zeep patching for better `xsd:any` field parsing and automatic flattening, applied at (>= [`v0.0.4`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.0.4)) |
159
+ | `capture_xml` | `bool` | ❌ No | `False` | Enable XML capture plugin for debugging SOAP requests/responses, applied at (>= [`v0.0.6`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.0.6)) |
160
+ | `wsdl_dir` | `str` | ❌ No | `None` | Custom WSDL directory path for using external WSDL files instead of built-in ones (e.g., `/path/to/custom/wsdl`), applied at (>= [`v0.1.0`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.1.0)) |
153
161
 
154
162
  </details>
155
163
 
@@ -279,6 +287,33 @@ if client.xml_plugin:
279
287
  > - `save_to_file(request_file, response_file)` - Save XML to files
280
288
  > - `clear_history()` - Clear captured history
281
289
 
290
+ **Custom WSDL Directory:**
291
+ ```python
292
+ from onvif import ONVIFClient
293
+
294
+ # Use custom WSDL files instead of built-in ones
295
+ client = ONVIFClient(
296
+ "192.168.1.17",
297
+ 80,
298
+ "admin",
299
+ "password",
300
+ wsdl_dir="/path/to/custom/wsdl" # Custom WSDL directory
301
+ )
302
+
303
+ # All services will automatically use custom WSDL files
304
+ device = client.devicemgmt()
305
+ media = client.media()
306
+ ptz = client.ptz()
307
+
308
+ # The custom WSDL directory should have a flat structure:
309
+ # /path/to/custom/wsdl/
310
+ # ├── devicemgmt.wsdl
311
+ # ├── media.wsdl
312
+ # ├── ptz.wsdl
313
+ # ├── imaging.wsdl
314
+ # └── ... (other WSDL files)
315
+ ```
316
+
282
317
  </details>
283
318
 
284
319
  <details>
@@ -298,7 +333,8 @@ client = ONVIFClient(
298
333
  use_https=True, # Secure communication
299
334
  verify_ssl=True, # Verify certificates (default)
300
335
  apply_patch=True, # Enhanced parsing (default)
301
- capture_xml=False # Disable debug mode (default)
336
+ capture_xml=False, # Disable debug mode (default)
337
+ wsdl_dir=None # Use built-in WSDL files (default)
302
338
  )
303
339
  ```
304
340
  </details>
@@ -308,12 +344,13 @@ client = ONVIFClient(
308
344
  - **Authentication:** This library uses **WS-UsernameToken with Digest** authentication by default, which is the standard for ONVIF devices.
309
345
  - **Patching:** The `apply_patch=True` (default) enables custom zeep patching that improves `xsd:any` field parsing. This is recommended for better compatibility with ONVIF responses.
310
346
  - **XML Capture:** Only use `capture_xml=True` during development/debugging as it increases memory usage and may expose sensitive data in logs.
347
+ - **Custom WSDL:** Use `wsdl_dir` parameter to specify a custom directory containing WSDL files. The directory should have a flat structure with WSDL files directly in the root (e.g., `/path/to/custom/wsdl/devicemgmt.wsdl`, `/path/to/custom/wsdl/media.wsdl`, etc.).
311
348
  - **Cache Location:** Disk cache (when using `CacheMode.DB` or `CacheMode.ALL`) is stored in `~/.onvif-python/onvif_zeep_cache.sqlite`.
312
349
 
313
350
  ## Service Discovery: Understanding Device Capabilities
314
351
 
315
352
  > [!WARNING]
316
- > Before performing any operations on an ONVIF device, it is highly recommended to discover which services are available and supported by the device. This library automatically uses `GetServices` during initialization to discover service endpoints, but you can also query services manually for detailed information including capabilities.
353
+ > Before performing any operations on an ONVIF device, it is highly recommended to discover which services are available and supported by the device. This library automatically performs comprehensive service discovery during initialization using a robust fallback mechanism.
317
354
 
318
355
  **Why discover device services?**
319
356
 
@@ -324,50 +361,84 @@ client = ONVIFClient(
324
361
 
325
362
  **How service discovery works in this library:**
326
363
 
327
- The `ONVIFClient` automatically calls `GetServices` during initialization to build a service map. This map is used internally to resolve service endpoints:
364
+ The `ONVIFClient` uses a **3-tier discovery approach** to maximize device compatibility:
365
+
366
+ 1. **GetServices (Preferred)** - Tries `GetServices` first for detailed service information
367
+ 2. **GetCapabilities (Fallback)** - Falls back to `GetCapabilities` if `GetServices` is not supported
368
+ 3. **Default URLs (Final Fallback)** - Uses standard ONVIF URLs as last resort
328
369
 
329
370
  ```python
330
371
  from onvif import ONVIFClient
331
372
 
332
373
  client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
333
374
 
334
- # Access the discovered services
335
- print(client.services)
336
- # Example output: [{'Namespace': 'http://www.onvif.org/ver10/device/wsdl', 'XAddr': '...', 'Version': {...}}, ...]
337
-
338
- # Check the service map (namespace -> XAddr mapping)
339
- print(client._service_map)
340
- # Example output: {'http://www.onvif.org/ver10/media/wsdl': 'http://192.168.1.17:8000/onvif/Media', ...}
375
+ # Check what discovery method was used
376
+ if client.services:
377
+ print("Service discovery: GetServices (preferred)")
378
+ print("Discovered services:", len(client.services))
379
+ print("Service map:", client._service_map)
380
+ elif client.capabilities:
381
+ print("Service discovery: GetCapabilities (fallback)")
382
+ print("Available capabilities:", client.capabilities)
383
+ else:
384
+ print("Service discovery: Using default URLs")
341
385
  ```
342
386
 
343
- **Get detailed service information with capabilities:**
387
+ **Why this approach?**
344
388
 
345
- If you need detailed capability information for each service, call `GetServices` with `IncludeCapability=True`:
389
+ - **GetServices** provides the most accurate and detailed service information, but it's **optional** in the ONVIF specification
390
+ - **GetCapabilities** is **mandatory** for all ONVIF-compliant devices, ensuring broader compatibility
391
+ - **Default URLs** guarantee basic connectivity even with non-compliant devices
392
+
393
+ **Get detailed service information:**
394
+
395
+ If you need comprehensive service details, you can manually call `GetServices` with capabilities:
346
396
 
347
397
  ```python
348
398
  device = client.devicemgmt()
349
- services = device.GetServices(IncludeCapability=True)
350
-
351
- for service in services:
352
- print(f"Service: {service.Namespace}")
353
- print(f"Endpoint: {service.XAddr}")
354
- print(f"Version: {service.Version.Major}.{service.Version.Minor}")
355
- if hasattr(service, 'Capabilities') and service.Capabilities:
356
- print(f"Capabilities: {service.Capabilities}")
399
+
400
+ try:
401
+ # Try to get detailed service information
402
+ services = device.GetServices(IncludeCapability=True)
403
+ for service in services:
404
+ print(f"Service: {service.Namespace}")
405
+ print(f"XAddr: {service.XAddr}")
406
+ if hasattr(service, 'Capabilities'):
407
+ print(f"Capabilities: {service.Capabilities}")
408
+ except Exception:
409
+ # Fallback to GetCapabilities for legacy devices
410
+ capabilities = device.GetCapabilities()
411
+ print("Capabilities:", capabilities)
357
412
  ```
358
413
 
359
- **Alternative: Use GetCapabilities for legacy compatibility:**
414
+ **Access capabilities information:**
360
415
 
361
- For backward compatibility or when you need a quick overview of major service categories, you can still use `GetCapabilities`:
416
+ When using `GetCapabilities` fallback, you can access capability information:
362
417
 
363
418
  ```python
364
- capabilities = client.devicemgmt().GetCapabilities()
365
- print(capabilities)
366
- # Example output: {'Media': {'XAddr': '...', ...}, 'PTZ': {...}, 'Events': {...}, ...}
419
+ device = client.devicemgmt()
420
+
421
+ try:
422
+ # Get capabilities information from device
423
+ capabilities = device.GetCapabilities()
424
+
425
+ # Main services (always available)
426
+ print("Media XAddr:", getattr(capabilities.Media, 'XAddr', 'Not available'))
427
+ print("PTZ XAddr:", getattr(capabilities.PTZ, 'XAddr', 'Not available'))
428
+
429
+ # Extension services (device-dependent)
430
+ ext = getattr(capabilities, 'Extension', None)
431
+ if ext:
432
+ print("DeviceIO XAddr:", getattr(ext.DeviceIO, 'XAddr', 'Not available'))
433
+ print("Recording XAddr:", getattr(ext.Recording, 'XAddr', 'Not available'))
434
+ print("Search XAddr:", getattr(ext.Search, 'XAddr', 'Not available'))
435
+ print("Replay XAddr:", getattr(ext.Replay, 'XAddr', 'Not available'))
436
+ except Exception as e:
437
+ print(f"Error getting capabilities: {e}")
367
438
  ```
368
439
 
369
440
  > [!TIP]
370
- > The library handles service discovery automatically, so you typically don't need to call `GetServices` manually unless you need detailed capability information or want to refresh the service list after device configuration changes.
441
+ > The library handles service discovery automatically with intelligent fallback. You typically don't need to call discovery methods manually unless you need detailed capability information or want to refresh the service list after device configuration changes.
371
442
 
372
443
  ## Tested Devices
373
444
 
@@ -520,10 +591,10 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
520
591
 
521
592
  ## Future Improvements (Stay tuned and star ⭐ this repo)
522
593
 
523
- - [x] Add debugging mode with raw xml on SOAP requests and responses. ([c258162](https://github.com/nirsimetri/onvif-python/commit/c258162))
524
- - [ ] Add functionality for `ONVIFClient` to accept a custom `wsdl_path` service.
525
- - [ ] Add asynchronous (async/await) support for non-blocking ONVIF operations and concurrent device communication.
594
+ - [x] ~~Add debugging mode with raw xml on SOAP requests and responses.~~ ([c258162](https://github.com/nirsimetri/onvif-python/commit/c258162))
595
+ - [x] ~~Add functionality for `ONVIFClient` to accept a custom `wsdl_dir` service.~~ ([65f2570](https://github.com/nirsimetri/onvif-python/commit/65f257092e4c9daa23dd0d00825ed38a45d23b70))
526
596
  - [ ] Add `ONVIF CLI` program to interact directly with ONVIF devices via terminal.
597
+ - [ ] Add asynchronous (async/await) support for non-blocking ONVIF operations and concurrent device communication.
527
598
  - [ ] Implement structured data models for ONVIF Schemas using [xsdata](https://github.com/tefra/xsdata).
528
599
  - [ ] Integrate [xmltodict](https://github.com/martinblech/xmltodict) for simplified XML parsing and conversion.
529
600
  - [ ] Enhance documentation with API references and diagrams (not from [AI Wiki](https://deepwiki.com/nirsimetri/onvif-python)).