onvif-python 0.1.7__tar.gz → 0.1.8__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 (130) hide show
  1. {onvif_python-0.1.7/onvif_python.egg-info → onvif_python-0.1.8}/PKG-INFO +21 -9
  2. {onvif_python-0.1.7 → onvif_python-0.1.8}/README.md +14 -6
  3. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/cli/interactive.py +2 -2
  4. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/cli/main.py +2 -2
  5. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/cli/utils.py +4 -2
  6. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/client.py +101 -21
  7. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/operator.py +50 -0
  8. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/accesscontrol.py +2 -2
  9. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/accessrules.py +2 -2
  10. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/actionengine.py +2 -2
  11. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/analytics/analytics.py +2 -2
  12. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/analytics/ruleengine.py +2 -2
  13. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/analyticsdevice.py +2 -2
  14. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/appmgmt.py +2 -2
  15. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/authenticationbehavior.py +2 -2
  16. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/credential.py +2 -2
  17. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/deviceio.py +4 -4
  18. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/devicemgmt.py +4 -4
  19. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/display.py +2 -2
  20. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/doorcontrol.py +2 -2
  21. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/events/events.py +2 -2
  22. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/events/notification.py +2 -2
  23. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/events/pullpoint.py +2 -2
  24. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/events/subscription.py +2 -2
  25. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/imaging.py +2 -2
  26. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/media.py +2 -2
  27. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/media2.py +2 -2
  28. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/provisioning.py +2 -2
  29. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/ptz.py +2 -2
  30. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/receiver.py +2 -2
  31. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/recording.py +2 -2
  32. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/replay.py +2 -2
  33. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/schedule.py +2 -2
  34. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/search.py +2 -2
  35. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/security/advancedsecurity.py +2 -2
  36. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/security/authorizationserver.py +2 -2
  37. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/security/dot1x.py +2 -2
  38. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/security/jwt.py +2 -2
  39. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/security/keystore.py +2 -2
  40. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/security/mediasigning.py +2 -2
  41. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/security/tlsserver.py +2 -2
  42. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/thermal.py +2 -2
  43. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/uplink.py +2 -2
  44. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/utils/__init__.py +2 -0
  45. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/utils/discovery.py +25 -6
  46. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/utils/error_handlers.py +31 -4
  47. onvif_python-0.1.8/onvif/utils/exceptions.py +163 -0
  48. onvif_python-0.1.8/onvif/utils/service.py +108 -0
  49. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/utils/wsdl.py +144 -16
  50. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/utils/xml_capture.py +63 -15
  51. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/utils/zeep.py +144 -20
  52. {onvif_python-0.1.7 → onvif_python-0.1.8/onvif_python.egg-info}/PKG-INFO +21 -9
  53. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif_python.egg-info/SOURCES.txt +1 -0
  54. {onvif_python-0.1.7 → onvif_python-0.1.8}/pyproject.toml +7 -3
  55. {onvif_python-0.1.7 → onvif_python-0.1.8}/tests/test_client.py +3 -1
  56. onvif_python-0.1.7/onvif/utils/exceptions.py +0 -91
  57. {onvif_python-0.1.7 → onvif_python-0.1.8}/LICENSE.md +0 -0
  58. {onvif_python-0.1.7 → onvif_python-0.1.8}/MANIFEST.in +0 -0
  59. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/__init__.py +0 -0
  60. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/cli/__init__.py +0 -0
  61. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/cli/__main__.py +0 -0
  62. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/services/__init__.py +0 -0
  63. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
  64. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
  65. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
  66. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
  67. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
  68. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
  69. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
  70. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
  71. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
  72. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
  73. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
  74. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
  75. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
  76. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
  77. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
  78. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/display/display.wsdl +0 -0
  79. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
  80. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/display.wsdl +0 -0
  81. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
  82. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
  83. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
  84. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
  85. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
  86. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
  87. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
  88. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
  89. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
  90. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
  91. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
  92. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/receiver.wsdl +0 -0
  93. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/recording.wsdl +0 -0
  94. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/replay.wsdl +0 -0
  95. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
  96. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
  97. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/schema/common.xsd +0 -0
  98. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
  99. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
  100. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/search.wsdl +0 -0
  101. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
  102. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
  103. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
  104. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
  105. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
  106. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
  107. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
  108. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/index.html +0 -0
  109. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
  110. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
  111. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
  112. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
  113. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
  114. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
  115. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
  116. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
  117. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
  118. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
  119. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
  120. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
  121. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif_python.egg-info/dependency_links.txt +0 -0
  122. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif_python.egg-info/entry_points.txt +0 -0
  123. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif_python.egg-info/requires.txt +0 -0
  124. {onvif_python-0.1.7 → onvif_python-0.1.8}/onvif_python.egg-info/top_level.txt +0 -0
  125. {onvif_python-0.1.7 → onvif_python-0.1.8}/setup.cfg +0 -0
  126. {onvif_python-0.1.7 → onvif_python-0.1.8}/setup.py +0 -0
  127. {onvif_python-0.1.7 → onvif_python-0.1.8}/tests/test_core.py +0 -0
  128. {onvif_python-0.1.7 → onvif_python-0.1.8}/tests/test_error_handlers.py +0 -0
  129. {onvif_python-0.1.7 → onvif_python-0.1.8}/tests/test_exceptions.py +0 -0
  130. {onvif_python-0.1.7 → onvif_python-0.1.8}/tests/test_wsdl_map.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onvif-python
3
- Version: 0.1.7
4
- Summary: A comprehensive and developer-friendly Python library for working with ONVIF-compliant devices
3
+ Version: 0.1.8
4
+ Summary: A modern Python library for ONVIF-compliant devices
5
5
  Author-email: Nirsimetri Technologies® <open@nirsimetri.com>
6
6
  Maintainer: Kaburagi
7
7
  License-Expression: MIT
@@ -11,8 +11,12 @@ Project-URL: Changelog, https://github.com/nirsimetri/onvif-python/releases
11
11
  Keywords: onvif,Camera,onvif-python,ip-camera,cctv,soap,surveillance,cctv cameras,soap-client,onvif-client,onvif-library,onvif-specifications
12
12
  Classifier: Development Status :: 5 - Production/Stable
13
13
  Classifier: Environment :: Console
14
- Classifier: Programming Language :: Python :: 3
15
14
  Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
16
20
  Classifier: Operating System :: OS Independent
17
21
  Classifier: Intended Audience :: Developers
18
22
  Classifier: Intended Audience :: Information Technology
@@ -37,15 +41,23 @@ Requires-Dist: black; extra == "dev"
37
41
  Requires-Dist: flake8; extra == "dev"
38
42
  Dynamic: license-file
39
43
 
40
- # ONVIF Python
44
+ <h1 align="center">ONVIF Python</h1>
41
45
 
42
- [![Codacy grade](https://img.shields.io/codacy/grade/bff08a94e4d447b690cea49c6594826d?label=Project%20Quality&logo=codacy)](https://app.codacy.com/gh/nirsimetri/onvif-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
46
+ <div align="center">
47
+
48
+ [![Codacy grade](https://img.shields.io/codacy/grade/bff08a94e4d447b690cea49c6594826d?label=Code%20Quality&logo=codacy)](https://app.codacy.com/gh/nirsimetri/onvif-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
43
49
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/nirsimetri/onvif-python)
44
- [![PyPI](https://img.shields.io/badge/PyPI-0.1.7-orange?logo=archive)](https://pypi.org/project/onvif-python/)
50
+ [![PyPI](https://img.shields.io/badge/PyPI-0.1.8-orange?logo=archive)](https://pypi.org/project/onvif-python/)
45
51
  [![Downloads](https://img.shields.io/pypi/dm/onvif-python?label=Downloads&color=red)](https://clickpy.clickhouse.com/dashboard/onvif-python)
46
52
  <br>
47
53
  [![Build](https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml/badge.svg?branch=main)](https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml)
48
54
  [![Upload Python Package](https://github.com/nirsimetri/onvif-python/actions/workflows/python-publish.yml/badge.svg)](https://github.com/nirsimetri/onvif-python/actions/workflows/python-publish.yml)
55
+ </div>
56
+
57
+ <h1 align="center">
58
+ <img src="https://raw.githubusercontent.com/nirsimetri/onvif-python/refs/heads/main/assets/carbon_onvif.png" alt="onvif" width="700px">
59
+ <br>
60
+ </h1>
49
61
 
50
62
  **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.
51
63
 
@@ -187,7 +199,7 @@ Explore more advanced usage and service-specific operations in the [`examples/`]
187
199
  ## ONVIF CLI
188
200
 
189
201
  > [!NOTE]
190
- > The CLI is automatically installed when you install the `onvif-python` see [Installation](#installation). This feature has been available since `onvif-python` version [`0.1.1`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.1.1).
202
+ > The CLI is automatically installed when you install the `onvif-python` see [Installation](#installation). This feature has been available since `onvif-python` version [`>=0.1.1`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.1.1).
191
203
 
192
204
  ![Windows](https://img.shields.io/badge/Windows-0078D6?style=plastic&logo=gitforwindows&logoColor=white)
193
205
  ![Linux](https://img.shields.io/badge/Linux-FCC624?style=plastic&logo=linux&logoColor=black)
@@ -242,7 +254,7 @@ usage: onvif [-h] [--host HOST] [--port PORT] [--username USERNAME] [--password
242
254
  [--health-check-interval HEALTH_CHECK_INTERVAL] [--version]
243
255
  [service] [method] [params ...]
244
256
 
245
- ONVIF Terminal Client — v0.1.7
257
+ ONVIF Terminal Client — v0.1.8
246
258
  https://github.com/nirsimetri/onvif-python
247
259
 
248
260
  positional arguments:
@@ -306,7 +318,7 @@ Examples:
306
318
  <summary><b>2. Interactive Shell</b></summary>
307
319
 
308
320
  ```bash
309
- ONVIF Interactive Shell — v0.1.7
321
+ ONVIF Interactive Shell — v0.1.8
310
322
  https://github.com/nirsimetri/onvif-python
311
323
 
312
324
  Basic Commands:
@@ -1,12 +1,20 @@
1
- # ONVIF Python
1
+ <h1 align="center">ONVIF Python</h1>
2
2
 
3
- [![Codacy grade](https://img.shields.io/codacy/grade/bff08a94e4d447b690cea49c6594826d?label=Project%20Quality&logo=codacy)](https://app.codacy.com/gh/nirsimetri/onvif-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
3
+ <div align="center">
4
+
5
+ [![Codacy grade](https://img.shields.io/codacy/grade/bff08a94e4d447b690cea49c6594826d?label=Code%20Quality&logo=codacy)](https://app.codacy.com/gh/nirsimetri/onvif-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
4
6
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/nirsimetri/onvif-python)
5
- [![PyPI](https://img.shields.io/badge/PyPI-0.1.7-orange?logo=archive)](https://pypi.org/project/onvif-python/)
7
+ [![PyPI](https://img.shields.io/badge/PyPI-0.1.8-orange?logo=archive)](https://pypi.org/project/onvif-python/)
6
8
  [![Downloads](https://img.shields.io/pypi/dm/onvif-python?label=Downloads&color=red)](https://clickpy.clickhouse.com/dashboard/onvif-python)
7
9
  <br>
8
10
  [![Build](https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml/badge.svg?branch=main)](https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml)
9
11
  [![Upload Python Package](https://github.com/nirsimetri/onvif-python/actions/workflows/python-publish.yml/badge.svg)](https://github.com/nirsimetri/onvif-python/actions/workflows/python-publish.yml)
12
+ </div>
13
+
14
+ <h1 align="center">
15
+ <img src="https://raw.githubusercontent.com/nirsimetri/onvif-python/refs/heads/main/assets/carbon_onvif.png" alt="onvif" width="700px">
16
+ <br>
17
+ </h1>
10
18
 
11
19
  **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.
12
20
 
@@ -148,7 +156,7 @@ Explore more advanced usage and service-specific operations in the [`examples/`]
148
156
  ## ONVIF CLI
149
157
 
150
158
  > [!NOTE]
151
- > The CLI is automatically installed when you install the `onvif-python` see [Installation](#installation). This feature has been available since `onvif-python` version [`0.1.1`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.1.1).
159
+ > The CLI is automatically installed when you install the `onvif-python` see [Installation](#installation). This feature has been available since `onvif-python` version [`>=0.1.1`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.1.1).
152
160
 
153
161
  ![Windows](https://img.shields.io/badge/Windows-0078D6?style=plastic&logo=gitforwindows&logoColor=white)
154
162
  ![Linux](https://img.shields.io/badge/Linux-FCC624?style=plastic&logo=linux&logoColor=black)
@@ -203,7 +211,7 @@ usage: onvif [-h] [--host HOST] [--port PORT] [--username USERNAME] [--password
203
211
  [--health-check-interval HEALTH_CHECK_INTERVAL] [--version]
204
212
  [service] [method] [params ...]
205
213
 
206
- ONVIF Terminal Client — v0.1.7
214
+ ONVIF Terminal Client — v0.1.8
207
215
  https://github.com/nirsimetri/onvif-python
208
216
 
209
217
  positional arguments:
@@ -267,7 +275,7 @@ Examples:
267
275
  <summary><b>2. Interactive Shell</b></summary>
268
276
 
269
277
  ```bash
270
- ONVIF Interactive Shell — v0.1.7
278
+ ONVIF Interactive Shell — v0.1.8
271
279
  https://github.com/nirsimetri/onvif-python
272
280
 
273
281
  Basic Commands:
@@ -191,7 +191,7 @@ class InteractiveShell(cmd.Cmd):
191
191
  " / __ \\/ | / / | / / _/ ____/",
192
192
  " / / / / |/ /| | / // // /_ ",
193
193
  "/ /_/ / /| / | |/ // // __/ ",
194
- "\\____/_/ |_/ |___/___/_/ v0.1.7",
194
+ "\\____/_/ |_/ |___/___/_/ v0.1.8",
195
195
  " ",
196
196
  ]
197
197
 
@@ -1385,7 +1385,7 @@ class InteractiveShell(cmd.Cmd):
1385
1385
  super().do_help(line)
1386
1386
  else:
1387
1387
  help_text = f"""
1388
- {colorize('ONVIF Interactive Shell — v0.1.7', 'cyan')}\n{colorize('https://github.com/nirsimetri/onvif-python', 'white')}
1388
+ {colorize('ONVIF Interactive Shell — v0.1.8', 'cyan')}\n{colorize('https://github.com/nirsimetri/onvif-python', 'white')}
1389
1389
 
1390
1390
  {colorize('Basic Commands:', 'yellow')}
1391
1391
  capabilities, caps - Show device capabilities
@@ -17,7 +17,7 @@ def create_parser():
17
17
  """Create argument parser for ONVIF CLI"""
18
18
  parser = argparse.ArgumentParser(
19
19
  prog="onvif",
20
- description=f"{colorize('ONVIF Terminal Client', 'yellow')} — v0.1.7\nhttps://github.com/nirsimetri/onvif-python",
20
+ description=f"{colorize('ONVIF Terminal Client', 'yellow')} — v0.1.8\nhttps://github.com/nirsimetri/onvif-python",
21
21
  formatter_class=argparse.RawDescriptionHelpFormatter,
22
22
  epilog=f"""
23
23
  Examples:
@@ -149,7 +149,7 @@ def main():
149
149
 
150
150
  # Show ONVIF CLI version
151
151
  if args.version:
152
- print(colorize("0.1.7", "yellow"))
152
+ print(colorize("0.1.8", "yellow"))
153
153
  sys.exit(0)
154
154
 
155
155
  # Validate arguments early (before discovery)
@@ -317,7 +317,8 @@ def get_method_documentation(service_obj, method_name: str) -> Optional[Dict[str
317
317
  doc_text = colorize("No description available.", "reset")
318
318
 
319
319
  # 2. Get parameters from Python method signature using inspect
320
- method = getattr(service_obj, method_name)
320
+ # Use object.__getattribute__ to bypass ONVIFService wrapper and get original method
321
+ method = object.__getattribute__(service_obj, method_name)
321
322
  sig = inspect.signature(method)
322
323
  for param in sig.parameters.values():
323
324
  if param.name != "self":
@@ -331,7 +332,8 @@ def get_method_documentation(service_obj, method_name: str) -> Optional[Dict[str
331
332
  except (etree.ParseError, FileNotFoundError, AttributeError, ValueError):
332
333
  # Fallback in case of any error, still try to get params
333
334
  try:
334
- method = getattr(service_obj, method_name)
335
+ # Use object.__getattribute__ to bypass ONVIFService wrapper and get original method
336
+ method = object.__getattribute__(service_obj, method_name)
335
337
  sig = inspect.signature(method)
336
338
  for param in sig.parameters.values():
337
339
  if param.name != "self":
@@ -1,6 +1,7 @@
1
1
  # onvif/client.py
2
2
 
3
3
  from urllib.parse import urlparse, urlunparse
4
+ from functools import wraps
4
5
 
5
6
  from .services import (
6
7
  Device,
@@ -41,10 +42,56 @@ from .services import (
41
42
  MediaSigning,
42
43
  )
43
44
  from .operator import CacheMode
44
- from .utils import ONVIFWSDL, ZeepPatcher, XMLCapturePlugin
45
+ from .utils import ONVIFWSDL, ZeepPatcher, XMLCapturePlugin, ONVIFOperationException
46
+
47
+
48
+ def service(func):
49
+ """Decorator to wrap service accessor methods with ONVIFOperationException handling.
50
+
51
+ This decorator catches any exception raised during service initialization and
52
+ wraps it in ONVIFOperationException for consistent error handling across all
53
+ ONVIF client service accessors.
54
+
55
+ Args:
56
+ func: Service accessor method to wrap
57
+
58
+ Returns:
59
+ Wrapped function that handles exceptions
60
+
61
+ Raises:
62
+ ONVIFOperationException: If service initialization fails
63
+ """
64
+
65
+ @wraps(func)
66
+ def wrapper(self, *args, **kwargs):
67
+ try:
68
+ return func(self, *args, **kwargs)
69
+ except ONVIFOperationException:
70
+ # Re-raise ONVIFOperationException as-is to avoid double-wrapping
71
+ raise
72
+ except Exception as e:
73
+ # Wrap any other exception in ONVIFOperationException
74
+ raise ONVIFOperationException(func.__name__, e)
75
+
76
+ return wrapper
45
77
 
46
78
 
47
79
  class ONVIFClient:
80
+ """ONVIF Client for communicating with ONVIF-compliant devices.
81
+
82
+ This is the main class for interacting with ONVIF devices. It provides access to
83
+ all ONVIF services including Device Management, Media, PTZ, Events, Analytics, and more.
84
+
85
+ The client automatically discovers available services on the device using GetServices
86
+ or GetCapabilities, and provides lazy initialization for service endpoints.
87
+
88
+ Attributes:
89
+ services: List of available services from GetServices response
90
+ capabilities: Device capabilities from GetCapabilities response (fallback)
91
+ xml_plugin: XML capture plugin for debugging (if capture_xml=True)
92
+ wsdl_dir: Custom WSDL directory path (if provided)
93
+ """
94
+
48
95
  def __init__(
49
96
  self,
50
97
  host: str,
@@ -90,7 +137,8 @@ class ONVIFClient:
90
137
  }
91
138
 
92
139
  # Device Management (Core) service is always available
93
- self._devicemgmt = Device(**self.common_args)
140
+ self._devicemgmt = None
141
+ self._devicemgmt = self.devicemgmt()
94
142
 
95
143
  # Try to retrieve device services and create namespace -> XAddr mapping
96
144
  self.services = None
@@ -280,11 +328,15 @@ class ONVIFClient:
280
328
 
281
329
  # Core (Device Management)
282
330
 
331
+ @service
283
332
  def devicemgmt(self):
333
+ if self._devicemgmt is None:
334
+ self._devicemgmt = Device(**self.common_args)
284
335
  return self._devicemgmt
285
336
 
286
337
  # Core (Events)
287
338
 
339
+ @service
288
340
  def events(self):
289
341
  if self._events is None:
290
342
  self._events = Events(
@@ -292,18 +344,16 @@ class ONVIFClient:
292
344
  )
293
345
  return self._events
294
346
 
347
+ @service
295
348
  def pullpoint(self, SubscriptionRef):
296
349
  xaddr = None
297
- try:
298
- addr_obj = SubscriptionRef["SubscriptionReference"]["Address"]
299
- if isinstance(addr_obj, dict) and "_value_1" in addr_obj:
300
- xaddr = addr_obj["_value_1"]
301
- elif hasattr(addr_obj, "_value_1"):
302
- xaddr = addr_obj._value_1
350
+ addr_obj = SubscriptionRef["SubscriptionReference"]["Address"]
351
+ if isinstance(addr_obj, dict) and "_value_1" in addr_obj:
352
+ xaddr = addr_obj["_value_1"]
353
+ elif hasattr(addr_obj, "_value_1"):
354
+ xaddr = addr_obj._value_1
303
355
 
304
- xaddr = self._rewrite_xaddr_if_needed(xaddr)
305
- except Exception:
306
- pass
356
+ xaddr = self._rewrite_xaddr_if_needed(xaddr)
307
357
 
308
358
  if not xaddr:
309
359
  raise RuntimeError(
@@ -315,6 +365,7 @@ class ONVIFClient:
315
365
 
316
366
  return self._pullpoints[xaddr]
317
367
 
368
+ @service
318
369
  def notification(self):
319
370
  if self._notification is None:
320
371
  self._notification = Notification(
@@ -322,18 +373,16 @@ class ONVIFClient:
322
373
  )
323
374
  return self._notification
324
375
 
376
+ @service
325
377
  def subscription(self, SubscriptionRef):
326
378
  xaddr = None
327
- try:
328
- addr_obj = SubscriptionRef["SubscriptionReference"]["Address"]
329
- if isinstance(addr_obj, dict) and "_value_1" in addr_obj:
330
- xaddr = addr_obj["_value_1"]
331
- elif hasattr(addr_obj, "_value_1"):
332
- xaddr = addr_obj._value_1
379
+ addr_obj = SubscriptionRef["SubscriptionReference"]["Address"]
380
+ if isinstance(addr_obj, dict) and "_value_1" in addr_obj:
381
+ xaddr = addr_obj["_value_1"]
382
+ elif hasattr(addr_obj, "_value_1"):
383
+ xaddr = addr_obj._value_1
333
384
 
334
- xaddr = self._rewrite_xaddr_if_needed(xaddr)
335
- except Exception:
336
- pass
385
+ xaddr = self._rewrite_xaddr_if_needed(xaddr)
337
386
 
338
387
  if not xaddr:
339
388
  raise RuntimeError(
@@ -347,6 +396,7 @@ class ONVIFClient:
347
396
 
348
397
  # Imaging
349
398
 
399
+ @service
350
400
  def imaging(self):
351
401
  if self._imaging is None:
352
402
  self._imaging = Imaging(
@@ -356,6 +406,7 @@ class ONVIFClient:
356
406
 
357
407
  # Media
358
408
 
409
+ @service
359
410
  def media(self):
360
411
  if self._media is None:
361
412
  self._media = Media(
@@ -363,6 +414,7 @@ class ONVIFClient:
363
414
  )
364
415
  return self._media
365
416
 
417
+ @service
366
418
  def media2(self):
367
419
  if self._media2 is None:
368
420
  self._media2 = Media2(
@@ -372,6 +424,7 @@ class ONVIFClient:
372
424
 
373
425
  # PTZ
374
426
 
427
+ @service
375
428
  def ptz(self):
376
429
  if self._ptz is None:
377
430
  self._ptz = PTZ(xaddr=self._get_xaddr("ptz", "PTZ"), **self.common_args)
@@ -379,6 +432,7 @@ class ONVIFClient:
379
432
 
380
433
  # DeviceIO
381
434
 
435
+ @service
382
436
  def deviceio(self):
383
437
  if self._deviceio is None:
384
438
  self._deviceio = DeviceIO(
@@ -388,6 +442,7 @@ class ONVIFClient:
388
442
 
389
443
  # Display
390
444
 
445
+ @service
391
446
  def display(self):
392
447
  if self._display is None:
393
448
  self._display = Display(
@@ -397,6 +452,7 @@ class ONVIFClient:
397
452
 
398
453
  # Analytics
399
454
 
455
+ @service
400
456
  def analytics(self):
401
457
  if self._analytics is None:
402
458
  self._analytics = Analytics(
@@ -404,6 +460,7 @@ class ONVIFClient:
404
460
  )
405
461
  return self._analytics
406
462
 
463
+ @service
407
464
  def ruleengine(self):
408
465
  if self._ruleengine is None:
409
466
  self._ruleengine = RuleEngine(
@@ -411,6 +468,7 @@ class ONVIFClient:
411
468
  )
412
469
  return self._ruleengine
413
470
 
471
+ @service
414
472
  def analyticsdevice(self):
415
473
  if self._analyticsdevice is None:
416
474
  self._analyticsdevice = AnalyticsDevice(
@@ -421,6 +479,7 @@ class ONVIFClient:
421
479
 
422
480
  # PACS
423
481
 
482
+ @service
424
483
  def accesscontrol(self):
425
484
  if self._accesscontrol is None:
426
485
  self._accesscontrol = AccessControl(
@@ -429,6 +488,7 @@ class ONVIFClient:
429
488
  )
430
489
  return self._accesscontrol
431
490
 
491
+ @service
432
492
  def doorcontrol(self):
433
493
  if self._doorcontrol is None:
434
494
  self._doorcontrol = DoorControl(
@@ -438,6 +498,7 @@ class ONVIFClient:
438
498
 
439
499
  # AccessRules
440
500
 
501
+ @service
441
502
  def accessrules(self):
442
503
  if self._accessrules is None:
443
504
  self._accessrules = AccessRules(
@@ -447,6 +508,7 @@ class ONVIFClient:
447
508
 
448
509
  # ActionEngine
449
510
 
511
+ @service
450
512
  def actionengine(self):
451
513
  if self._actionengine is None:
452
514
  self._actionengine = ActionEngine(
@@ -457,6 +519,7 @@ class ONVIFClient:
457
519
 
458
520
  # AppManagement
459
521
 
522
+ @service
460
523
  def appmanagement(self):
461
524
  if self._appmanagement is None:
462
525
  self._appmanagement = AppManagement(
@@ -467,6 +530,7 @@ class ONVIFClient:
467
530
 
468
531
  # AuthenticationBehavior
469
532
 
533
+ @service
470
534
  def authenticationbehavior(self):
471
535
  if self._authenticationbehavior is None:
472
536
  self._authenticationbehavior = AuthenticationBehavior(
@@ -479,6 +543,7 @@ class ONVIFClient:
479
543
 
480
544
  # Credential
481
545
 
546
+ @service
482
547
  def credential(self):
483
548
  if self._credential is None:
484
549
  self._credential = Credential(
@@ -489,6 +554,7 @@ class ONVIFClient:
489
554
 
490
555
  # Recording
491
556
 
557
+ @service
492
558
  def recording(self):
493
559
  if self._recording is None:
494
560
  self._recording = Recording(
@@ -499,6 +565,7 @@ class ONVIFClient:
499
565
 
500
566
  # Replay
501
567
 
568
+ @service
502
569
  def replay(self):
503
570
  if self._replay is None:
504
571
  self._replay = Replay(
@@ -509,6 +576,7 @@ class ONVIFClient:
509
576
 
510
577
  # Provisioning
511
578
 
579
+ @service
512
580
  def provisioning(self):
513
581
  if self._provisioning is None:
514
582
  self._provisioning = Provisioning(
@@ -519,6 +587,7 @@ class ONVIFClient:
519
587
 
520
588
  # Receiver
521
589
 
590
+ @service
522
591
  def receiver(self):
523
592
  if self._receiver is None:
524
593
  self._receiver = Receiver(
@@ -529,6 +598,7 @@ class ONVIFClient:
529
598
 
530
599
  # Schedule
531
600
 
601
+ @service
532
602
  def schedule(self):
533
603
  if self._schedule is None:
534
604
  self._schedule = Schedule(
@@ -537,8 +607,9 @@ class ONVIFClient:
537
607
  )
538
608
  return self._schedule
539
609
 
540
- # Search
610
+ # Search Recording
541
611
 
612
+ @service
542
613
  def search(self):
543
614
  if self._search is None:
544
615
  self._search = Search(
@@ -549,6 +620,7 @@ class ONVIFClient:
549
620
 
550
621
  # Thermal
551
622
 
623
+ @service
552
624
  def thermal(self):
553
625
  if self._thermal is None:
554
626
  self._thermal = Thermal(
@@ -559,6 +631,7 @@ class ONVIFClient:
559
631
 
560
632
  # Uplink
561
633
 
634
+ @service
562
635
  def uplink(self):
563
636
  if self._uplink is None:
564
637
  self._uplink = Uplink(
@@ -569,6 +642,7 @@ class ONVIFClient:
569
642
 
570
643
  # Security - AdvancedSecurity
571
644
 
645
+ @service
572
646
  def security(self):
573
647
  if self._security is None:
574
648
  self._security = AdvancedSecurity(
@@ -577,30 +651,35 @@ class ONVIFClient:
577
651
  )
578
652
  return self._security
579
653
 
654
+ @service
580
655
  def jwt(self, xaddr):
581
656
  if self._jwt is None:
582
657
  xaddr = self._rewrite_xaddr_if_needed(xaddr)
583
658
  self._jwt = JWT(xaddr=xaddr, **self.common_args)
584
659
  return self._jwt
585
660
 
661
+ @service
586
662
  def keystore(self, xaddr):
587
663
  if self._keystore is None:
588
664
  xaddr = self._rewrite_xaddr_if_needed(xaddr)
589
665
  self._keystore = Keystore(xaddr=xaddr, **self.common_args)
590
666
  return self._keystore
591
667
 
668
+ @service
592
669
  def tlsserver(self, xaddr):
593
670
  if self._tlsserver is None:
594
671
  xaddr = self._rewrite_xaddr_if_needed(xaddr)
595
672
  self._tlsserver = TLSServer(xaddr=xaddr, **self.common_args)
596
673
  return self._tlsserver
597
674
 
675
+ @service
598
676
  def dot1x(self, xaddr):
599
677
  if self._dot1x is None:
600
678
  xaddr = self._rewrite_xaddr_if_needed(xaddr)
601
679
  self._dot1x = Dot1X(xaddr=xaddr, **self.common_args)
602
680
  return self._dot1x
603
681
 
682
+ @service
604
683
  def authorizationserver(self, xaddr):
605
684
  if self._authorizationserver is None:
606
685
  xaddr = self._rewrite_xaddr_if_needed(xaddr)
@@ -609,6 +688,7 @@ class ONVIFClient:
609
688
  )
610
689
  return self._authorizationserver
611
690
 
691
+ @service
612
692
  def mediasigning(self, xaddr):
613
693
  if self._mediasigning is None:
614
694
  xaddr = self._rewrite_xaddr_if_needed(xaddr)
@@ -15,6 +15,18 @@ from .utils import ONVIFOperationException, ZeepPatcher
15
15
 
16
16
 
17
17
  class CacheMode(Enum):
18
+ """WSDL caching strategies for ONVIF client performance optimization.
19
+
20
+ Different caching modes provide trade-offs between performance, memory usage,
21
+ and disk storage. Choose based on your application's requirements.
22
+
23
+ Attributes:
24
+ ALL: Maximum performance with both memory and disk caching
25
+ DB: Disk-only caching for persistent storage
26
+ MEM: Memory-only caching for temporary sessions
27
+ NONE: No caching, always fetch fresh WSDLs
28
+ """
29
+
18
30
  ALL = "all" # CachingClient + SqliteCache →
19
31
  # (+) Fast startup (WSDL/schema cached in memory + disk), great for multi-device and long-running apps
20
32
  # (-) More complex, extra overhead on both disk and memory
@@ -37,6 +49,27 @@ class CacheMode(Enum):
37
49
 
38
50
 
39
51
  class ONVIFOperator:
52
+ """Low-level ONVIF service operator using Zeep SOAP client.
53
+
54
+ This class handles the actual SOAP communication with ONVIF devices. It manages
55
+ WSDL loading, service binding, authentication, caching, and error handling.
56
+
57
+ ONVIFOperator is typically used internally by service classes (Device, Media, PTZ, etc.)
58
+ and is not meant to be instantiated directly by end users. Use ONVIFClient instead.
59
+
60
+ Attributes:
61
+ wsdl_path (str): Path to the WSDL file
62
+ host (str): Device hostname or IP address
63
+ port (int): Device port number
64
+ username (str): ONVIF username
65
+ password (str): ONVIF password
66
+ timeout (int): Request timeout in seconds
67
+ apply_patch (bool): Whether to apply xsd:any flattening patch
68
+ address (str): Service endpoint URL (XAddr)
69
+ client: Zeep SOAP client instance
70
+ service: Zeep service proxy for making SOAP calls
71
+ """
72
+
40
73
  def __init__(
41
74
  self,
42
75
  wsdl_path: str,
@@ -124,6 +157,23 @@ class ONVIFOperator:
124
157
  # logging.debug(f"ONVIFOperator initialized {binding} at {self.address}")
125
158
 
126
159
  def call(self, method: str, *args, **kwargs):
160
+ """Call an ONVIF service operation.
161
+
162
+ This method invokes a SOAP operation on the ONVIF device service and handles
163
+ errors gracefully. It automatically flattens xsd:any fields in the response
164
+ when apply_patch is enabled.
165
+
166
+ Args:
167
+ method: Name of the ONVIF operation to call (e.g., "GetDeviceInformation")
168
+ *args: Positional arguments to pass to the operation
169
+ **kwargs: Keyword arguments to pass to the operation
170
+
171
+ Returns:
172
+ The operation result, with xsd:any fields flattened if apply_patch=True
173
+
174
+ Raises:
175
+ ONVIFOperationException: If the operation fails (wraps original exception)
176
+ """
127
177
  try:
128
178
  func = getattr(self.service, method)
129
179
  except AttributeError as e:
@@ -1,10 +1,10 @@
1
1
  # onvif/services/accesscontrol.py
2
2
 
3
3
  from ..operator import ONVIFOperator
4
- from ..utils import ONVIFWSDL
4
+ from ..utils import ONVIFWSDL, ONVIFService
5
5
 
6
6
 
7
- class AccessControl:
7
+ class AccessControl(ONVIFService):
8
8
  def __init__(self, xaddr=None, **kwargs):
9
9
  # References:
10
10
  # - ONVIF Release 2.3 (May 2013) Release Notes
@@ -1,10 +1,10 @@
1
1
  # onvif/services/accessrules.py
2
2
 
3
3
  from ..operator import ONVIFOperator
4
- from ..utils import ONVIFWSDL
4
+ from ..utils import ONVIFWSDL, ONVIFService
5
5
 
6
6
 
7
- class AccessRules:
7
+ class AccessRules(ONVIFService):
8
8
  def __init__(self, xaddr=None, **kwargs):
9
9
  # References:
10
10
  # - ONVIF Release 2.6 (June 2015) Release Notes