onvif-python 0.0.4__tar.gz → 0.0.5__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 (119) hide show
  1. {onvif_python-0.0.4/onvif_python.egg-info → onvif_python-0.0.5}/PKG-INFO +49 -16
  2. {onvif_python-0.0.4 → onvif_python-0.0.5}/README.md +48 -15
  3. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/__init__.py +2 -2
  4. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/client.py +56 -41
  5. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/operator.py +1 -1
  6. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/devicemgmt.py +1 -1
  7. onvif_python-0.0.5/onvif/utils/zeep.py +272 -0
  8. {onvif_python-0.0.4 → onvif_python-0.0.5/onvif_python.egg-info}/PKG-INFO +49 -16
  9. {onvif_python-0.0.4 → onvif_python-0.0.5}/pyproject.toml +1 -1
  10. onvif_python-0.0.4/onvif/utils/zeep.py +0 -230
  11. {onvif_python-0.0.4 → onvif_python-0.0.5}/LICENSE.md +0 -0
  12. {onvif_python-0.0.4 → onvif_python-0.0.5}/MANIFEST.in +0 -0
  13. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/__init__.py +0 -0
  14. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/accesscontrol.py +0 -0
  15. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/accessrules.py +0 -0
  16. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/actionengine.py +0 -0
  17. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/analytics/analytics.py +0 -0
  18. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/analytics/ruleengine.py +0 -0
  19. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/analyticsdevice.py +0 -0
  20. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/appmgmt.py +0 -0
  21. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/authenticationbehavior.py +0 -0
  22. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/credential.py +0 -0
  23. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/deviceio.py +0 -0
  24. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/display.py +0 -0
  25. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/doorcontrol.py +0 -0
  26. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/events/events.py +0 -0
  27. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/events/notification.py +0 -0
  28. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/events/pullpoint.py +0 -0
  29. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/events/subscription.py +0 -0
  30. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/imaging.py +0 -0
  31. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/media.py +0 -0
  32. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/media2.py +0 -0
  33. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/provisioning.py +0 -0
  34. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/ptz.py +0 -0
  35. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/receiver.py +0 -0
  36. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/recording.py +0 -0
  37. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/replay.py +0 -0
  38. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/schedule.py +0 -0
  39. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/search.py +0 -0
  40. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/security/advancedsecurity.py +0 -0
  41. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/security/authorizationserver.py +0 -0
  42. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/security/dot1x.py +0 -0
  43. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/security/jwt.py +0 -0
  44. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/security/keystore.py +0 -0
  45. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/security/mediasigning.py +0 -0
  46. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/security/tlsserver.py +0 -0
  47. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/thermal.py +0 -0
  48. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/uplink.py +0 -0
  49. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/utils/__init__.py +0 -0
  50. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/utils/exceptions.py +0 -0
  51. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/utils/wsdl.py +0 -0
  52. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
  53. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
  54. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
  55. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
  56. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
  57. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
  58. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
  59. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
  60. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
  61. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
  62. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
  63. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
  64. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
  65. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
  66. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
  67. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/display/display.wsdl +0 -0
  68. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
  69. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/display.wsdl +0 -0
  70. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
  71. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
  72. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
  73. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
  74. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
  75. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
  76. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
  77. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
  78. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
  79. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
  80. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
  81. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/receiver.wsdl +0 -0
  82. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/recording.wsdl +0 -0
  83. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/replay.wsdl +0 -0
  84. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
  85. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
  86. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/schema/common.xsd +0 -0
  87. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
  88. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
  89. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/search.wsdl +0 -0
  90. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
  91. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
  92. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
  93. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
  94. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
  95. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
  96. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
  97. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/analytics/index.html +0 -0
  98. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
  99. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
  100. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
  101. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
  102. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
  103. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
  104. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
  105. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
  106. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
  107. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
  108. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
  109. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
  110. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif_python.egg-info/SOURCES.txt +0 -0
  111. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif_python.egg-info/dependency_links.txt +0 -0
  112. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif_python.egg-info/requires.txt +0 -0
  113. {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif_python.egg-info/top_level.txt +0 -0
  114. {onvif_python-0.0.4 → onvif_python-0.0.5}/setup.cfg +0 -0
  115. {onvif_python-0.0.4 → onvif_python-0.0.5}/setup.py +0 -0
  116. {onvif_python-0.0.4 → onvif_python-0.0.5}/tests/test_client.py +0 -0
  117. {onvif_python-0.0.4 → onvif_python-0.0.5}/tests/test_core.py +0 -0
  118. {onvif_python-0.0.4 → onvif_python-0.0.5}/tests/test_exceptions.py +0 -0
  119. {onvif_python-0.0.4 → onvif_python-0.0.5}/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.4
3
+ Version: 0.0.5
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
@@ -41,9 +41,9 @@ Dynamic: license-file
41
41
 
42
42
  [![License](https://img.shields.io/badge/License-MIT-blue)](https://github.com/nirsimetri/onvif-python?tab=MIT-1-ov-file)
43
43
  [![DeepWiki](https://img.shields.io/badge/DeepWiki-AI%20Wiki-orange)](https://deepwiki.com/nirsimetri/onvif-python)
44
- [![Release](https://img.shields.io/badge/Release-v0.0.4-red?logo=archive)](https://github.com/nirsimetri/onvif-python/releases)
44
+ [![Release](https://img.shields.io/badge/Release-v0.0.5-red?logo=archive)](https://github.com/nirsimetri/onvif-python/releases)
45
45
  <br>
46
- [![PyPI](https://img.shields.io/badge/PyPI-0.0.4-yellow?logo=archive)](https://pypi.org/project/onvif-python/)
46
+ [![PyPI](https://img.shields.io/badge/PyPI-0.0.5-yellow?logo=archive)](https://pypi.org/project/onvif-python/)
47
47
  [![Downloads](https://img.shields.io/pypi/dm/onvif-python?label=PyPI%20Downloads)](https://clickpy.clickhouse.com/dashboard/onvif-python)
48
48
 
49
49
  Are you having trouble finding a Python ONVIF library that supports your device?
@@ -148,32 +148,64 @@ Explore more advanced usage and service-specific operations in the [`examples/`]
148
148
  > [!IMPORTANT]
149
149
  > If you're new to ONVIF and want to learn more, we highly recommend taking the official free online course provided by ONVIF at [Introduction to ONVIF Course](https://www.onvif.org/about/introduction-to-onvif-course). Please note that we are not endorsed or sponsored by ONVIF, see [Legal Notice](#legal-notice) for details.
150
150
 
151
- ## Device Verification: Why Use GetCapabilities First?
151
+ ## Service Discovery: Understanding Device Capabilities
152
152
 
153
153
  > [!WARNING]
154
- > Before performing any operations on an ONVIF device, it is highly recommended to verify which capabilities and services are available and supported by the device using the `GetCapabilities` method from `devicemgmt()` service instance. This step ensures that your application interacts only with features that the device actually implements, preventing errors and improving compatibility.
154
+ > 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.
155
155
 
156
- **Why verify device capabilities with GetCapabilities?**
156
+ **Why discover device services?**
157
157
 
158
- - **Device Diversity:** Not all ONVIF devices support every capability or service. Capabilities may vary by manufacturer, model, firmware, or configuration.
159
- - **Error Prevention:** Attempting to use unsupported features can result in failed requests, exceptions, or undefined behavior.
160
- - **Dynamic Feature Detection:** Devices may enable or disable capabilities over time (e.g., after firmware updates or configuration changes).
161
- - **Optimized Integration:** By checking available capabilities, your application can adapt its workflow and UI to match the device's actual features.
158
+ - **Device Diversity:** Not all ONVIF devices support every service. Available services may vary by manufacturer, model, firmware, or configuration.
159
+ - **Error Prevention:** Attempting to use unsupported services can result in failed requests, exceptions, or undefined behavior.
160
+ - **Dynamic Feature Detection:** Devices may enable or disable services over time (e.g., after firmware updates or configuration changes).
161
+ - **Optimized Integration:** By checking available services, your application can adapt its workflow and UI to match the device's actual features.
162
162
 
163
- **How to verify device capabilities:**
163
+ **How service discovery works in this library:**
164
164
 
165
- Call `GetCapabilities` on your `devicemgmt()` instance:
165
+ The `ONVIFClient` automatically calls `GetServices` during initialization to build a service map. This map is used internally to resolve service endpoints:
166
166
 
167
167
  ```python
168
168
  from onvif import ONVIFClient
169
169
 
170
170
  client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
171
+
172
+ # Access the discovered services
173
+ print(client.services)
174
+ # Example output: [{'Namespace': 'http://www.onvif.org/ver10/device/wsdl', 'XAddr': '...', 'Version': {...}}, ...]
175
+
176
+ # Check the service map (namespace -> XAddr mapping)
177
+ print(client._service_map)
178
+ # Example output: {'http://www.onvif.org/ver10/media/wsdl': 'http://192.168.1.17:8000/onvif/Media', ...}
179
+ ```
180
+
181
+ **Get detailed service information with capabilities:**
182
+
183
+ If you need detailed capability information for each service, call `GetServices` with `IncludeCapability=True`:
184
+
185
+ ```python
186
+ device = client.devicemgmt()
187
+ services = device.GetServices(IncludeCapability=True)
188
+
189
+ for service in services:
190
+ print(f"Service: {service.Namespace}")
191
+ print(f"Endpoint: {service.XAddr}")
192
+ print(f"Version: {service.Version.Major}.{service.Version.Minor}")
193
+ if hasattr(service, 'Capabilities') and service.Capabilities:
194
+ print(f"Capabilities: {service.Capabilities}")
195
+ ```
196
+
197
+ **Alternative: Use GetCapabilities for legacy compatibility:**
198
+
199
+ For backward compatibility or when you need a quick overview of major service categories, you can still use `GetCapabilities`:
200
+
201
+ ```python
171
202
  capabilities = client.devicemgmt().GetCapabilities()
172
203
  print(capabilities)
173
- # Example output: {'Media': {'XAddr': 'http://192.168.1.17:8000/onvif/media_service', ...}, 'PTZ': {...}, ...}
204
+ # Example output: {'Media': {'XAddr': '...', ...}, 'PTZ': {...}, 'Events': {...}, ...}
174
205
  ```
175
206
 
176
- Review the returned dictionary to determine which capabilities and services (e.g., Media, PTZ, Analytics) are available before invoking further operations.
207
+ > [!TIP]
208
+ > 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.
177
209
 
178
210
  ## Tested Devices
179
211
 
@@ -283,6 +315,7 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
283
315
  client.events() # root binding
284
316
  client.pullpoint(subscription) # sub-binding (dynamic, via SubscriptionReference.Address)
285
317
  client.subscription(subscription) # sub-binding (dynamic, via SubscriptionReference.Address)
318
+ client.notification() # sub-binding accessor
286
319
  ```
287
320
 
288
321
  2. **Security (Advanced Security)**
@@ -290,7 +323,6 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
290
323
  - **Sub-bindings:**
291
324
  - `AuthorizationServerBinding`
292
325
  - `KeystoreBinding`
293
- - `CredentialBinding`
294
326
  - `JWTBinding`
295
327
  - `Dot1XBinding`
296
328
  - `TLSServerBinding`
@@ -300,7 +332,7 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
300
332
  ```python
301
333
  client.security() # root binding
302
334
  client.authorizationserver(xaddr) # sub-binding accessor (requires xAddr)
303
- client.keystore(xaddr)
335
+ client.keystore(xaddr) # ..
304
336
  client.jwt(xaddr)
305
337
  client.dot1x(xaddr)
306
338
  client.tlsserver(xaddr)
@@ -326,6 +358,7 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
326
358
 
327
359
  ## Future Improvements (Stay tuned and star ⭐ this repo)
328
360
 
361
+ - [ ] Add ONVIF CLI program to interact directly with ONVIF devices via terminal.
329
362
  - [ ] Implement structured data models for ONVIF Schemas using [xsdata](https://github.com/tefra/xsdata).
330
363
  - [ ] Integrate [xmltodict](https://github.com/martinblech/xmltodict) for simplified XML parsing and conversion.
331
364
  - [ ] Add functionality for `ONVIFClient` to accept a custom `wsdl_path` service.
@@ -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.4-red?logo=archive)](https://github.com/nirsimetri/onvif-python/releases)
5
+ [![Release](https://img.shields.io/badge/Release-v0.0.5-red?logo=archive)](https://github.com/nirsimetri/onvif-python/releases)
6
6
  <br>
7
- [![PyPI](https://img.shields.io/badge/PyPI-0.0.4-yellow?logo=archive)](https://pypi.org/project/onvif-python/)
7
+ [![PyPI](https://img.shields.io/badge/PyPI-0.0.5-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
  Are you having trouble finding a Python ONVIF library that supports your device?
@@ -109,32 +109,64 @@ Explore more advanced usage and service-specific operations in the [`examples/`]
109
109
  > [!IMPORTANT]
110
110
  > If you're new to ONVIF and want to learn more, we highly recommend taking the official free online course provided by ONVIF at [Introduction to ONVIF Course](https://www.onvif.org/about/introduction-to-onvif-course). Please note that we are not endorsed or sponsored by ONVIF, see [Legal Notice](#legal-notice) for details.
111
111
 
112
- ## Device Verification: Why Use GetCapabilities First?
112
+ ## Service Discovery: Understanding Device Capabilities
113
113
 
114
114
  > [!WARNING]
115
- > Before performing any operations on an ONVIF device, it is highly recommended to verify which capabilities and services are available and supported by the device using the `GetCapabilities` method from `devicemgmt()` service instance. This step ensures that your application interacts only with features that the device actually implements, preventing errors and improving compatibility.
115
+ > 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.
116
116
 
117
- **Why verify device capabilities with GetCapabilities?**
117
+ **Why discover device services?**
118
118
 
119
- - **Device Diversity:** Not all ONVIF devices support every capability or service. Capabilities may vary by manufacturer, model, firmware, or configuration.
120
- - **Error Prevention:** Attempting to use unsupported features can result in failed requests, exceptions, or undefined behavior.
121
- - **Dynamic Feature Detection:** Devices may enable or disable capabilities over time (e.g., after firmware updates or configuration changes).
122
- - **Optimized Integration:** By checking available capabilities, your application can adapt its workflow and UI to match the device's actual features.
119
+ - **Device Diversity:** Not all ONVIF devices support every service. Available services may vary by manufacturer, model, firmware, or configuration.
120
+ - **Error Prevention:** Attempting to use unsupported services can result in failed requests, exceptions, or undefined behavior.
121
+ - **Dynamic Feature Detection:** Devices may enable or disable services over time (e.g., after firmware updates or configuration changes).
122
+ - **Optimized Integration:** By checking available services, your application can adapt its workflow and UI to match the device's actual features.
123
123
 
124
- **How to verify device capabilities:**
124
+ **How service discovery works in this library:**
125
125
 
126
- Call `GetCapabilities` on your `devicemgmt()` instance:
126
+ The `ONVIFClient` automatically calls `GetServices` during initialization to build a service map. This map is used internally to resolve service endpoints:
127
127
 
128
128
  ```python
129
129
  from onvif import ONVIFClient
130
130
 
131
131
  client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
132
+
133
+ # Access the discovered services
134
+ print(client.services)
135
+ # Example output: [{'Namespace': 'http://www.onvif.org/ver10/device/wsdl', 'XAddr': '...', 'Version': {...}}, ...]
136
+
137
+ # Check the service map (namespace -> XAddr mapping)
138
+ print(client._service_map)
139
+ # Example output: {'http://www.onvif.org/ver10/media/wsdl': 'http://192.168.1.17:8000/onvif/Media', ...}
140
+ ```
141
+
142
+ **Get detailed service information with capabilities:**
143
+
144
+ If you need detailed capability information for each service, call `GetServices` with `IncludeCapability=True`:
145
+
146
+ ```python
147
+ device = client.devicemgmt()
148
+ services = device.GetServices(IncludeCapability=True)
149
+
150
+ for service in services:
151
+ print(f"Service: {service.Namespace}")
152
+ print(f"Endpoint: {service.XAddr}")
153
+ print(f"Version: {service.Version.Major}.{service.Version.Minor}")
154
+ if hasattr(service, 'Capabilities') and service.Capabilities:
155
+ print(f"Capabilities: {service.Capabilities}")
156
+ ```
157
+
158
+ **Alternative: Use GetCapabilities for legacy compatibility:**
159
+
160
+ For backward compatibility or when you need a quick overview of major service categories, you can still use `GetCapabilities`:
161
+
162
+ ```python
132
163
  capabilities = client.devicemgmt().GetCapabilities()
133
164
  print(capabilities)
134
- # Example output: {'Media': {'XAddr': 'http://192.168.1.17:8000/onvif/media_service', ...}, 'PTZ': {...}, ...}
165
+ # Example output: {'Media': {'XAddr': '...', ...}, 'PTZ': {...}, 'Events': {...}, ...}
135
166
  ```
136
167
 
137
- Review the returned dictionary to determine which capabilities and services (e.g., Media, PTZ, Analytics) are available before invoking further operations.
168
+ > [!TIP]
169
+ > 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.
138
170
 
139
171
  ## Tested Devices
140
172
 
@@ -244,6 +276,7 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
244
276
  client.events() # root binding
245
277
  client.pullpoint(subscription) # sub-binding (dynamic, via SubscriptionReference.Address)
246
278
  client.subscription(subscription) # sub-binding (dynamic, via SubscriptionReference.Address)
279
+ client.notification() # sub-binding accessor
247
280
  ```
248
281
 
249
282
  2. **Security (Advanced Security)**
@@ -251,7 +284,6 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
251
284
  - **Sub-bindings:**
252
285
  - `AuthorizationServerBinding`
253
286
  - `KeystoreBinding`
254
- - `CredentialBinding`
255
287
  - `JWTBinding`
256
288
  - `Dot1XBinding`
257
289
  - `TLSServerBinding`
@@ -261,7 +293,7 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
261
293
  ```python
262
294
  client.security() # root binding
263
295
  client.authorizationserver(xaddr) # sub-binding accessor (requires xAddr)
264
- client.keystore(xaddr)
296
+ client.keystore(xaddr) # ..
265
297
  client.jwt(xaddr)
266
298
  client.dot1x(xaddr)
267
299
  client.tlsserver(xaddr)
@@ -287,6 +319,7 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
287
319
 
288
320
  ## Future Improvements (Stay tuned and star ⭐ this repo)
289
321
 
322
+ - [ ] Add ONVIF CLI program to interact directly with ONVIF devices via terminal.
290
323
  - [ ] Implement structured data models for ONVIF Schemas using [xsdata](https://github.com/tefra/xsdata).
291
324
  - [ ] Integrate [xmltodict](https://github.com/martinblech/xmltodict) for simplified XML parsing and conversion.
292
325
  - [ ] Add functionality for `ONVIFClient` to accept a custom `wsdl_path` service.
@@ -12,6 +12,6 @@ __all__ = [
12
12
  "ONVIFWSDL",
13
13
  "ONVIFOperationException",
14
14
  "apply_patch",
15
- "remove_patch",
15
+ "remove_patch",
16
16
  "is_patched",
17
- ]
17
+ ]
@@ -41,7 +41,11 @@ from .services import (
41
41
  MediaSigning,
42
42
  )
43
43
  from .operator import CacheMode
44
- from .utils.zeep import apply_patch as _apply_zeep_patch, remove_patch as _remove_zeep_patch
44
+ from .utils.zeep import (
45
+ apply_patch as _apply_zeep_patch,
46
+ remove_patch as _remove_zeep_patch,
47
+ )
48
+ from .utils.wsdl import ONVIFWSDL
45
49
 
46
50
 
47
51
  class ONVIFClient:
@@ -58,13 +62,13 @@ class ONVIFClient:
58
62
  apply_patch=True,
59
63
  ):
60
64
  self.apply_patch = apply_patch
61
-
65
+
62
66
  # Apply or remove zeep patch based on user preference
63
67
  if apply_patch:
64
68
  _apply_zeep_patch()
65
69
  else:
66
70
  _remove_zeep_patch()
67
-
71
+
68
72
  self.common_args = {
69
73
  "host": host,
70
74
  "port": port,
@@ -80,8 +84,15 @@ class ONVIFClient:
80
84
  # Device Management (Core) service is always available
81
85
  self._devicemgmt = Device(**self.common_args)
82
86
 
83
- # Retrieve device capabilities once and reuse
84
- self.capabilities = self._devicemgmt.GetCapabilities(Category="All")
87
+ # Retrieve device services once and create namespace -> XAddr mapping
88
+ self.services = self._devicemgmt.GetServices(IncludeCapability=False)
89
+ self._service_map = {}
90
+ for service in self.services:
91
+ namespace = getattr(service, "Namespace", None)
92
+ xaddr = getattr(service, "XAddr", None)
93
+
94
+ if namespace and xaddr:
95
+ self._service_map[namespace] = xaddr
85
96
 
86
97
  # Lazy init for other services
87
98
 
@@ -141,45 +152,49 @@ class ONVIFClient:
141
152
  self._authorizationserver = None
142
153
  self._mediasigning = None
143
154
 
144
-
145
155
  def _get_xaddr(self, service_name: str, service_path: str):
146
156
  """
147
- Resolve XAddr from GetCapabilities. Fallback to default if not present.
148
- Now supports nested Extension with _value_1 dict (after normalized parsing).
157
+ Resolve XAddr from GetServices based on namespace mapping.
158
+ Falls back to default URL if service not found.
149
159
  """
150
- svc = getattr(self.capabilities, service_name, None)
151
-
152
- # Step 1: check direct attribute (e.g. capabilities.Media)
153
- if svc and hasattr(svc, "XAddr"):
154
- xaddr = svc.XAddr
155
- else:
156
- # Step 2: try legacy Extension.service_name
157
- ext = getattr(self.capabilities, "Extension", None)
158
- if ext and hasattr(ext, service_name):
159
- svc = getattr(ext, service_name, None)
160
- xaddr = getattr(svc, "XAddr", None) if svc else None
161
- else:
162
- # Step 3: try new-style _value_1 dict inside Extension
163
- ext_dict = getattr(ext, "_value_1", {})
164
- xaddr = ext_dict.get(service_name, {}).get("XAddr") if isinstance(ext_dict, dict) else None
165
-
166
- if xaddr:
167
- parsed = urlparse(xaddr)
168
- # Host/port from device
169
- device_host = parsed.hostname
170
- device_port = parsed.port
171
- # Host/port from connection
172
- connect_host = self.common_args["host"]
173
- connect_port = self.common_args["port"]
174
- # if host/port differ, rewrite XAddr to use connection values
175
- if (device_host != connect_host) or (device_port != connect_port):
176
- protocol = "https" if self.common_args["use_https"] else "http"
177
- new_netloc = f"{connect_host}:{connect_port}"
178
- rewritten = urlunparse((protocol, new_netloc, parsed.path, "", "", ""))
179
- return rewritten
180
- return xaddr
181
-
182
- # Fallback default
160
+ # Get the namespace for this service from WSDL_MAP
161
+ try:
162
+ # Try to get the service definition from WSDL_MAP
163
+ # Most services use ver10, some use ver20
164
+ service_key = service_name.lower()
165
+ wsdl_def = None
166
+
167
+ # Try ver10 first, then ver20
168
+ if service_key in ONVIFWSDL.WSDL_MAP:
169
+ if "ver10" in ONVIFWSDL.WSDL_MAP[service_key]:
170
+ wsdl_def = ONVIFWSDL.WSDL_MAP[service_key]["ver10"]
171
+ elif "ver20" in ONVIFWSDL.WSDL_MAP[service_key]:
172
+ wsdl_def = ONVIFWSDL.WSDL_MAP[service_key]["ver20"]
173
+
174
+ if wsdl_def:
175
+ namespace = wsdl_def["namespace"]
176
+ xaddr = self._service_map.get(namespace)
177
+
178
+ if xaddr:
179
+ # Rewrite host/port if needed
180
+ parsed = urlparse(xaddr)
181
+ device_host = parsed.hostname
182
+ device_port = parsed.port
183
+ connect_host = self.common_args["host"]
184
+ connect_port = self.common_args["port"]
185
+
186
+ if (device_host != connect_host) or (device_port != connect_port):
187
+ protocol = "https" if self.common_args["use_https"] else "http"
188
+ new_netloc = f"{connect_host}:{connect_port}"
189
+ rewritten = urlunparse(
190
+ (protocol, new_netloc, parsed.path, "", "", "")
191
+ )
192
+ return rewritten
193
+ return xaddr
194
+ except Exception:
195
+ pass
196
+
197
+ # Fallback to default URL
183
198
  protocol = "https" if self.common_args["use_https"] else "http"
184
199
  return f"{protocol}://{self.common_args['host']}:{self.common_args['port']}/onvif/{service_path}"
185
200
 
@@ -125,7 +125,7 @@ class ONVIFOperator:
125
125
 
126
126
  try:
127
127
  result = func(*args, **kwargs)
128
- # Post-process to flatten xsd:any fields if enabled
128
+ # Post-process to flatten xsd:any fields if enabled (> v0.0.4 patch)
129
129
  if self.apply_flatten:
130
130
  return flatten_xsd_any_fields(result)
131
131
  return result
@@ -25,7 +25,7 @@ class Device:
25
25
  return self.operator.call("GetDeviceInformation")
26
26
 
27
27
  def SetSystemDateAndTime(
28
- self, DateTimeType, DaylightSavings=True, TimeZone=None, UTCDateTime=None
28
+ self, DateTimeType, DaylightSavings, TimeZone=None, UTCDateTime=None
29
29
  ):
30
30
  return self.operator.call(
31
31
  "SetSystemDateAndTime",