onvif-python 0.0.9__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.9/onvif_python.egg-info → onvif_python-0.1.0}/PKG-INFO +46 -9
  2. {onvif_python-0.0.9 → onvif_python-0.1.0}/README.md +45 -8
  3. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/client.py +54 -36
  4. onvif_python-0.1.0/onvif/utils/wsdl.py +350 -0
  5. {onvif_python-0.0.9 → onvif_python-0.1.0/onvif_python.egg-info}/PKG-INFO +46 -9
  6. {onvif_python-0.0.9 → onvif_python-0.1.0}/pyproject.toml +1 -1
  7. onvif_python-0.0.9/onvif/utils/wsdl.py +0 -297
  8. {onvif_python-0.0.9 → onvif_python-0.1.0}/LICENSE.md +0 -0
  9. {onvif_python-0.0.9 → onvif_python-0.1.0}/MANIFEST.in +0 -0
  10. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/__init__.py +0 -0
  11. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/operator.py +0 -0
  12. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/__init__.py +0 -0
  13. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/accesscontrol.py +0 -0
  14. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/accessrules.py +0 -0
  15. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/actionengine.py +0 -0
  16. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/analytics/analytics.py +0 -0
  17. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/analytics/ruleengine.py +0 -0
  18. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/analyticsdevice.py +0 -0
  19. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/appmgmt.py +0 -0
  20. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/authenticationbehavior.py +0 -0
  21. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/credential.py +0 -0
  22. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/deviceio.py +0 -0
  23. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/devicemgmt.py +0 -0
  24. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/display.py +0 -0
  25. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/doorcontrol.py +0 -0
  26. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/events/events.py +0 -0
  27. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/events/notification.py +0 -0
  28. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/events/pullpoint.py +0 -0
  29. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/events/subscription.py +0 -0
  30. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/imaging.py +0 -0
  31. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/media.py +0 -0
  32. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/media2.py +0 -0
  33. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/provisioning.py +0 -0
  34. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/ptz.py +0 -0
  35. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/receiver.py +0 -0
  36. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/recording.py +0 -0
  37. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/replay.py +0 -0
  38. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/schedule.py +0 -0
  39. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/search.py +0 -0
  40. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/security/advancedsecurity.py +0 -0
  41. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/security/authorizationserver.py +0 -0
  42. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/security/dot1x.py +0 -0
  43. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/security/jwt.py +0 -0
  44. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/security/keystore.py +0 -0
  45. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/security/mediasigning.py +0 -0
  46. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/security/tlsserver.py +0 -0
  47. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/thermal.py +0 -0
  48. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/uplink.py +0 -0
  49. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/utils/__init__.py +0 -0
  50. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/utils/error_handlers.py +0 -0
  51. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/utils/exceptions.py +0 -0
  52. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/utils/xml_capture.py +0 -0
  53. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/utils/zeep.py +0 -0
  54. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
  55. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
  56. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
  57. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
  58. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
  59. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
  60. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
  61. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
  62. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
  63. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
  64. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
  65. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
  66. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
  67. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
  68. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
  69. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/display/display.wsdl +0 -0
  70. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
  71. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/display.wsdl +0 -0
  72. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
  73. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
  74. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
  75. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
  76. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
  77. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
  78. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
  79. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
  80. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
  81. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
  82. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
  83. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/receiver.wsdl +0 -0
  84. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/recording.wsdl +0 -0
  85. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/replay.wsdl +0 -0
  86. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
  87. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
  88. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/schema/common.xsd +0 -0
  89. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
  90. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
  91. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/search.wsdl +0 -0
  92. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
  93. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
  94. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
  95. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
  96. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
  97. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
  98. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
  99. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/index.html +0 -0
  100. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
  101. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
  102. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
  103. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
  104. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
  105. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
  106. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
  107. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
  108. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
  109. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
  110. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
  111. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
  112. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif_python.egg-info/SOURCES.txt +0 -0
  113. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif_python.egg-info/dependency_links.txt +0 -0
  114. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif_python.egg-info/requires.txt +0 -0
  115. {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif_python.egg-info/top_level.txt +0 -0
  116. {onvif_python-0.0.9 → onvif_python-0.1.0}/setup.cfg +0 -0
  117. {onvif_python-0.0.9 → onvif_python-0.1.0}/setup.py +0 -0
  118. {onvif_python-0.0.9 → onvif_python-0.1.0}/tests/test_client.py +0 -0
  119. {onvif_python-0.0.9 → onvif_python-0.1.0}/tests/test_core.py +0 -0
  120. {onvif_python-0.0.9 → onvif_python-0.1.0}/tests/test_exceptions.py +0 -0
  121. {onvif_python-0.0.9 → onvif_python-0.1.0}/tests/test_wsdl_map.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onvif-python
3
- Version: 0.0.9
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
@@ -40,9 +40,9 @@ Dynamic: license-file
40
40
 
41
41
  [![License](https://img.shields.io/badge/License-MIT-blue)](https://github.com/nirsimetri/onvif-python?tab=MIT-1-ov-file)
42
42
  [![DeepWiki](https://img.shields.io/badge/DeepWiki-AI%20Wiki-orange)](https://deepwiki.com/nirsimetri/onvif-python)
43
- [![Release](https://img.shields.io/badge/Release-v0.0.9-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)
44
44
  <br>
45
- [![PyPI](https://img.shields.io/badge/PyPI-0.0.9-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/)
46
46
  [![Downloads](https://img.shields.io/pypi/dm/onvif-python?label=PyPI%20Downloads)](https://clickpy.clickhouse.com/dashboard/onvif-python)
47
47
 
48
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.
@@ -93,7 +93,14 @@ Create an instance of `ONVIFClient` by providing your device's IP address, port,
93
93
  ```python
94
94
  from onvif import ONVIFClient
95
95
 
96
+ # Basic connection
96
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
+ )
97
104
  ```
98
105
 
99
106
  **2. Create Service Instance**
@@ -186,8 +193,9 @@ The `ONVIFClient` class provides various configuration options to customize the
186
193
 
187
194
  | Parameter | Type | Required | Default | Description |
188
195
  |-----------|------|----------|---------|-------------|
189
- | `apply_patch` | `bool` | ❌ No | `True` | Enable zeep patching for better `xsd:any` field parsing and automatic flattening |
190
- | `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)) |
191
199
 
192
200
  </details>
193
201
 
@@ -317,6 +325,33 @@ if client.xml_plugin:
317
325
  > - `save_to_file(request_file, response_file)` - Save XML to files
318
326
  > - `clear_history()` - Clear captured history
319
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
+
320
355
  </details>
321
356
 
322
357
  <details>
@@ -336,7 +371,8 @@ client = ONVIFClient(
336
371
  use_https=True, # Secure communication
337
372
  verify_ssl=True, # Verify certificates (default)
338
373
  apply_patch=True, # Enhanced parsing (default)
339
- 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)
340
376
  )
341
377
  ```
342
378
  </details>
@@ -346,6 +382,7 @@ client = ONVIFClient(
346
382
  - **Authentication:** This library uses **WS-UsernameToken with Digest** authentication by default, which is the standard for ONVIF devices.
347
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.
348
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.).
349
386
  - **Cache Location:** Disk cache (when using `CacheMode.DB` or `CacheMode.ALL`) is stored in `~/.onvif-python/onvif_zeep_cache.sqlite`.
350
387
 
351
388
  ## Service Discovery: Understanding Device Capabilities
@@ -592,10 +629,10 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
592
629
 
593
630
  ## Future Improvements (Stay tuned and star ⭐ this repo)
594
631
 
595
- - [x] Add debugging mode with raw xml on SOAP requests and responses. ([c258162](https://github.com/nirsimetri/onvif-python/commit/c258162))
596
- - [ ] Add functionality for `ONVIFClient` to accept a custom `wsdl_path` service.
597
- - [ ] 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))
598
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.
599
636
  - [ ] Implement structured data models for ONVIF Schemas using [xsdata](https://github.com/tefra/xsdata).
600
637
  - [ ] Integrate [xmltodict](https://github.com/martinblech/xmltodict) for simplified XML parsing and conversion.
601
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.9-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.9-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,6 +344,7 @@ 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
@@ -554,10 +591,10 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
554
591
 
555
592
  ## Future Improvements (Stay tuned and star ⭐ this repo)
556
593
 
557
- - [x] Add debugging mode with raw xml on SOAP requests and responses. ([c258162](https://github.com/nirsimetri/onvif-python/commit/c258162))
558
- - [ ] Add functionality for `ONVIFClient` to accept a custom `wsdl_path` service.
559
- - [ ] 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))
560
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.
561
598
  - [ ] Implement structured data models for ONVIF Schemas using [xsdata](https://github.com/tefra/xsdata).
562
599
  - [ ] Integrate [xmltodict](https://github.com/martinblech/xmltodict) for simplified XML parsing and conversion.
563
600
  - [ ] Enhance documentation with API references and diagrams (not from [AI Wiki](https://deepwiki.com/nirsimetri/onvif-python)).
@@ -57,6 +57,7 @@ class ONVIFClient:
57
57
  verify_ssl=True,
58
58
  apply_patch=True,
59
59
  capture_xml=False,
60
+ wsdl_dir=None,
60
61
  ):
61
62
  # Apply or remove zeep patch based on user preference
62
63
  if apply_patch:
@@ -69,6 +70,11 @@ class ONVIFClient:
69
70
  if capture_xml:
70
71
  self.xml_plugin = XMLCapturePlugin()
71
72
 
73
+ # Store custom WSDL directory if provided
74
+ self.wsdl_dir = wsdl_dir
75
+ if wsdl_dir:
76
+ ONVIFWSDL.set_custom_wsdl_dir(wsdl_dir)
77
+
72
78
  # Pass to ONVIFOperator
73
79
  self.common_args = {
74
80
  "host": host,
@@ -114,9 +120,9 @@ class ONVIFClient:
114
120
  # Lazy init for other services
115
121
 
116
122
  self._events = None
117
- self._pullpoint = None
123
+ self._pullpoints = {} # Dictionary for multiple PullPoint instances
118
124
  self._notification = None
119
- self._subscription = None
125
+ self._subscriptions = {} # Dictionary for multiple Subscription instances
120
126
 
121
127
  self._imaging = None
122
128
 
@@ -286,24 +292,27 @@ class ONVIFClient:
286
292
  return self._events
287
293
 
288
294
  def pullpoint(self, SubscriptionRef):
289
- if self._pullpoint is None:
290
- xaddr = None
291
- try:
292
- addr_obj = SubscriptionRef["SubscriptionReference"]["Address"]
293
- if isinstance(addr_obj, dict) and "_value_1" in addr_obj:
294
- xaddr = addr_obj["_value_1"]
295
- elif hasattr(addr_obj, "_value_1"):
296
- xaddr = addr_obj._value_1
297
- except Exception:
298
- pass
299
-
300
- if not xaddr:
301
- raise RuntimeError(
302
- "SubscriptionReference.Address missing in subscription response"
303
- )
295
+ xaddr = None
296
+ try:
297
+ addr_obj = SubscriptionRef["SubscriptionReference"]["Address"]
298
+ if isinstance(addr_obj, dict) and "_value_1" in addr_obj:
299
+ xaddr = addr_obj["_value_1"]
300
+ elif hasattr(addr_obj, "_value_1"):
301
+ xaddr = addr_obj._value_1
302
+
303
+ xaddr = self._rewrite_xaddr_if_needed(xaddr)
304
+ except Exception:
305
+ pass
304
306
 
305
- self._pullpoint = PullPoint(xaddr=xaddr, **self.common_args)
306
- return self._pullpoint
307
+ if not xaddr:
308
+ raise RuntimeError(
309
+ "SubscriptionReference.Address missing in subscription response"
310
+ )
311
+
312
+ if xaddr not in self._pullpoints:
313
+ self._pullpoints[xaddr] = PullPoint(xaddr=xaddr, **self.common_args)
314
+
315
+ return self._pullpoints[xaddr]
307
316
 
308
317
  def notification(self):
309
318
  if self._notification is None:
@@ -313,24 +322,27 @@ class ONVIFClient:
313
322
  return self._notification
314
323
 
315
324
  def subscription(self, SubscriptionRef):
316
- if self._subscription is None:
317
- xaddr = None
318
- try:
319
- addr_obj = SubscriptionRef["SubscriptionReference"]["Address"]
320
- if isinstance(addr_obj, dict) and "_value_1" in addr_obj:
321
- xaddr = addr_obj["_value_1"]
322
- elif hasattr(addr_obj, "_value_1"):
323
- xaddr = addr_obj._value_1
324
- except Exception:
325
- pass
326
-
327
- if not xaddr:
328
- raise RuntimeError(
329
- "SubscriptionReference.Address missing in subscription response"
330
- )
325
+ xaddr = None
326
+ try:
327
+ addr_obj = SubscriptionRef["SubscriptionReference"]["Address"]
328
+ if isinstance(addr_obj, dict) and "_value_1" in addr_obj:
329
+ xaddr = addr_obj["_value_1"]
330
+ elif hasattr(addr_obj, "_value_1"):
331
+ xaddr = addr_obj._value_1
332
+
333
+ xaddr = self._rewrite_xaddr_if_needed(xaddr)
334
+ except Exception:
335
+ pass
331
336
 
332
- self._subscription = Subscription(xaddr=xaddr, **self.common_args)
333
- return self._subscription
337
+ if not xaddr:
338
+ raise RuntimeError(
339
+ "SubscriptionReference.Address missing in subscription response"
340
+ )
341
+
342
+ if xaddr not in self._subscriptions:
343
+ self._subscriptions[xaddr] = Subscription(xaddr=xaddr, **self.common_args)
344
+
345
+ return self._subscriptions[xaddr]
334
346
 
335
347
  # Imaging
336
348
 
@@ -566,26 +578,31 @@ class ONVIFClient:
566
578
 
567
579
  def jwt(self, xaddr):
568
580
  if self._jwt is None:
581
+ xaddr = self._rewrite_xaddr_if_needed(xaddr)
569
582
  self._jwt = JWT(xaddr=xaddr, **self.common_args)
570
583
  return self._jwt
571
584
 
572
585
  def keystore(self, xaddr):
573
586
  if self._keystore is None:
587
+ xaddr = self._rewrite_xaddr_if_needed(xaddr)
574
588
  self._keystore = Keystore(xaddr=xaddr, **self.common_args)
575
589
  return self._keystore
576
590
 
577
591
  def tlsserver(self, xaddr):
578
592
  if self._tlsserver is None:
593
+ xaddr = self._rewrite_xaddr_if_needed(xaddr)
579
594
  self._tlsserver = TLSServer(xaddr=xaddr, **self.common_args)
580
595
  return self._tlsserver
581
596
 
582
597
  def dot1x(self, xaddr):
583
598
  if self._dot1x is None:
599
+ xaddr = self._rewrite_xaddr_if_needed(xaddr)
584
600
  self._dot1x = Dot1X(xaddr=xaddr, **self.common_args)
585
601
  return self._dot1x
586
602
 
587
603
  def authorizationserver(self, xaddr):
588
604
  if self._authorizationserver is None:
605
+ xaddr = self._rewrite_xaddr_if_needed(xaddr)
589
606
  self._authorizationserver = AuthorizationServer(
590
607
  xaddr=xaddr, **self.common_args
591
608
  )
@@ -593,5 +610,6 @@ class ONVIFClient:
593
610
 
594
611
  def mediasigning(self, xaddr):
595
612
  if self._mediasigning is None:
613
+ xaddr = self._rewrite_xaddr_if_needed(xaddr)
596
614
  self._mediasigning = MediaSigning(xaddr=xaddr, **self.common_args)
597
615
  return self._mediasigning