onvif-python 0.3.1__tar.gz → 0.4.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 (161) hide show
  1. {onvif_python-0.3.1/onvif_python.egg-info → onvif_python-0.4.0}/PKG-INFO +18 -8
  2. {onvif_python-0.3.1 → onvif_python-0.4.0}/README.md +10 -4
  3. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/__init__.py +6 -2
  4. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/cli/helpers/discovery.py +38 -6
  5. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/cli/helpers/files.py +17 -15
  6. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/cli/helpers/messages.py +3 -4
  7. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/cli/helpers/products.py +2 -0
  8. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/cli/helpers/wsdl_parser.py +9 -4
  9. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/cli/interactive.py +7 -13
  10. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/cli/main.py +10 -4
  11. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/cli/utils.py +2 -0
  12. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/client.py +35 -25
  13. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/meta.py +2 -2
  14. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/operator.py +73 -41
  15. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/accesscontrol.py +2 -1
  16. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/accessrules.py +2 -1
  17. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/actionengine.py +2 -1
  18. onvif_python-0.4.0/onvif/services/analytics/__init__.py +1 -0
  19. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/analytics/analytics.py +2 -1
  20. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/analytics/ruleengine.py +2 -1
  21. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/analyticsdevice.py +2 -1
  22. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/appmgmt.py +2 -1
  23. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/authenticationbehavior.py +3 -2
  24. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/credential.py +2 -1
  25. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/deviceio.py +3 -1
  26. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/devicemgmt.py +5 -1
  27. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/display.py +2 -1
  28. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/doorcontrol.py +2 -1
  29. onvif_python-0.4.0/onvif/services/events/__init__.py +1 -0
  30. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/events/events.py +2 -1
  31. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/events/notification.py +5 -19
  32. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/events/pausable_subscription.py +9 -5
  33. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/events/pullpoint.py +2 -1
  34. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/events/subscription.py +6 -5
  35. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/imaging.py +6 -3
  36. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/media.py +2 -1
  37. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/media2.py +3 -1
  38. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/provisioning.py +3 -2
  39. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/ptz.py +2 -1
  40. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/receiver.py +2 -1
  41. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/recording.py +2 -1
  42. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/replay.py +2 -1
  43. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/schedule.py +2 -1
  44. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/search.py +9 -1
  45. onvif_python-0.4.0/onvif/services/security/__init__.py +1 -0
  46. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/security/advancedsecurity.py +3 -2
  47. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/security/authorizationserver.py +3 -2
  48. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/security/dot1x.py +3 -2
  49. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/security/jwt.py +3 -2
  50. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/security/keystore.py +3 -2
  51. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/security/mediasigning.py +3 -2
  52. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/security/tlsserver.py +3 -2
  53. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/thermal.py +2 -1
  54. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/uplink.py +2 -1
  55. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/utils/__init__.py +9 -4
  56. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/utils/discovery.py +132 -18
  57. onvif_python-0.4.0/onvif/utils/error_handlers.py +237 -0
  58. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/utils/exceptions.py +56 -41
  59. onvif_python-0.4.0/onvif/utils/plugins.py +500 -0
  60. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/utils/service.py +103 -84
  61. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/utils/wsdl.py +61 -32
  62. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/utils/zeep.py +262 -274
  63. {onvif_python-0.3.1 → onvif_python-0.4.0/onvif_python.egg-info}/PKG-INFO +18 -8
  64. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif_python.egg-info/SOURCES.txt +0 -1
  65. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif_python.egg-info/requires.txt +6 -0
  66. {onvif_python-0.3.1 → onvif_python-0.4.0}/pyproject.toml +42 -6
  67. {onvif_python-0.3.1 → onvif_python-0.4.0}/tests/test_client.py +44 -60
  68. {onvif_python-0.3.1 → onvif_python-0.4.0}/tests/test_core.py +13 -34
  69. {onvif_python-0.3.1 → onvif_python-0.4.0}/tests/test_error_handlers.py +86 -70
  70. {onvif_python-0.3.1 → onvif_python-0.4.0}/tests/test_exceptions.py +16 -16
  71. onvif_python-0.3.1/onvif/services/analytics/__init__.py +0 -0
  72. onvif_python-0.3.1/onvif/services/events/__init__.py +0 -0
  73. onvif_python-0.3.1/onvif/services/security/__init__.py +0 -0
  74. onvif_python-0.3.1/onvif/utils/error_handlers.py +0 -130
  75. onvif_python-0.3.1/onvif/utils/plugins.py +0 -172
  76. onvif_python-0.3.1/onvif/utils/xml_capture.py +0 -219
  77. {onvif_python-0.3.1 → onvif_python-0.4.0}/LICENSE.md +0 -0
  78. {onvif_python-0.3.1 → onvif_python-0.4.0}/MANIFEST.in +0 -0
  79. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/cli/__init__.py +0 -0
  80. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/cli/__main__.py +0 -0
  81. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/cli/helpers/__init__.py +0 -0
  82. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/cli/helpers/execution.py +0 -0
  83. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/db/companies.db +0 -0
  84. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/db/products.db +0 -0
  85. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/services/__init__.py +0 -0
  86. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/LICENSE.md +0 -0
  87. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/schema/common/b-2.xsd +0 -0
  88. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/schema/common/bf-2.xsd +0 -0
  89. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/schema/common/bw-2.wsdl +0 -0
  90. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/schema/common/include +0 -0
  91. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/schema/common/mpqf.xsd +0 -0
  92. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/schema/common/r-2.xsd +0 -0
  93. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/schema/common/rw-2.wsdl +0 -0
  94. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/schema/common/soap-envelope +0 -0
  95. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/schema/common/t-1.xsd +0 -0
  96. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/schema/common/ws-addr.xsd +0 -0
  97. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/schema/common/xml.xsd +0 -0
  98. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/schema/common/xmlmime +0 -0
  99. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
  100. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
  101. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
  102. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
  103. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
  104. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
  105. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
  106. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
  107. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
  108. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
  109. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
  110. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
  111. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
  112. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
  113. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
  114. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/display/display.wsdl +0 -0
  115. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
  116. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/display.wsdl +0 -0
  117. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
  118. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
  119. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
  120. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
  121. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
  122. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
  123. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
  124. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
  125. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
  126. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
  127. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
  128. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/receiver.wsdl +0 -0
  129. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/recording.wsdl +0 -0
  130. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/replay.wsdl +0 -0
  131. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
  132. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
  133. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/schema/common.xsd +0 -0
  134. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
  135. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
  136. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/search.wsdl +0 -0
  137. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
  138. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
  139. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
  140. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
  141. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
  142. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
  143. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
  144. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/analytics/index.html +0 -0
  145. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
  146. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
  147. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
  148. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
  149. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
  150. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
  151. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
  152. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
  153. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
  154. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
  155. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
  156. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
  157. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif_python.egg-info/dependency_links.txt +0 -0
  158. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif_python.egg-info/entry_points.txt +0 -0
  159. {onvif_python-0.3.1 → onvif_python-0.4.0}/onvif_python.egg-info/top_level.txt +0 -0
  160. {onvif_python-0.3.1 → onvif_python-0.4.0}/setup.cfg +0 -0
  161. {onvif_python-0.3.1 → onvif_python-0.4.0}/setup.py +0 -0
@@ -1,17 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onvif-python
3
- Version: 0.3.1
3
+ Version: 0.4.0
4
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
8
8
  Project-URL: Homepage, https://github.com/nirsimetri/onvif-python
9
- Project-URL: Documentation, https://deepwiki.com/nirsimetri/onvif-python
9
+ Project-URL: Documentation, https://nirsimetri.github.io/onvif-python
10
10
  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.9
15
14
  Classifier: Programming Language :: Python :: 3.10
16
15
  Classifier: Programming Language :: Python :: 3.11
17
16
  Classifier: Programming Language :: Python :: 3.12
@@ -30,7 +29,7 @@ Classifier: Topic :: Communications
30
29
  Classifier: Topic :: Text Processing :: Markup :: XML
31
30
  Classifier: Natural Language :: English
32
31
  Classifier: Natural Language :: Indonesian
33
- Requires-Python: >=3.9
32
+ Requires-Python: >=3.10
34
33
  Description-Content-Type: text/markdown
35
34
  License-File: LICENSE.md
36
35
  Requires-Dist: zeep>=4.3.0
@@ -38,18 +37,23 @@ Requires-Dist: requests>=2.32.0
38
37
  Requires-Dist: pyreadline3>=3.5.4; sys_platform == "win32"
39
38
  Provides-Extra: dev
40
39
  Requires-Dist: pytest>=7.0; extra == "dev"
40
+ Requires-Dist: pytest-cov; extra == "dev"
41
41
  Requires-Dist: black; extra == "dev"
42
42
  Requires-Dist: mypy; extra == "dev"
43
43
  Requires-Dist: isort; extra == "dev"
44
44
  Requires-Dist: pylint; extra == "dev"
45
45
  Requires-Dist: docformatter; extra == "dev"
46
+ Provides-Extra: docs
47
+ Requires-Dist: mkdocs-material; extra == "docs"
48
+ Requires-Dist: mkdocstrings[python]; extra == "docs"
49
+ Requires-Dist: mkdocs-git-authors-plugin; extra == "docs"
46
50
  Dynamic: license-file
47
51
 
48
52
  <h1 align="center">ONVIF Python</h1>
49
53
 
50
54
  <div align="center">
51
55
  <a href="https://app.codacy.com/gh/nirsimetri/onvif-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade"><img src="https://img.shields.io/codacy/grade/bff08a94e4d447b690cea49c6594826d?style=plastic&logo=codacy&label=Code%20Quality"/></a>
52
- <a href="https://pypi.org/project/onvif-python/"><img alt="PyPI Version" src="https://img.shields.io/badge/Version-0.3.1-orange?logo=python&logoColor=white&color=yellow&style=plastic"></a>
56
+ <a href="https://pypi.org/project/onvif-python/"><img alt="PyPI Version" src="https://img.shields.io/badge/Version-0.4.0-orange?logo=python&logoColor=white&color=yellow&style=plastic"></a>
53
57
  <a href="https://pepy.tech/projects/onvif-python"><img alt="Pepy Total Downloads" src="https://img.shields.io/pepy/dt/onvif-python?logo=pypi&logoColor=white&label=Downloads&color=red&style=plastic"></a>
54
58
  <br>
55
59
  <a href="https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml"><img alt="Build" src="https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml/badge.svg?branch=main"></a>
@@ -76,7 +80,7 @@ This library simplifies that process by wrapping SOAP communication into a clean
76
80
 
77
81
  ## Requirements
78
82
 
79
- - **Python**: 3.9 or higher
83
+ - **Python**: 3.10 or higher
80
84
  - **Dependencies**:
81
85
  - [`zeep>=4.3.0`](https://github.com/mvantellingen/python-zeep) - SOAP client for ONVIF communication
82
86
  - [`requests>=2.32.0`](https://github.com/psf/requests) - HTTP library for network requests
@@ -84,16 +88,22 @@ This library simplifies that process by wrapping SOAP communication into a clean
84
88
  ## Installation
85
89
 
86
90
  From official [PyPI](https://pypi.org/project/onvif-python/):
87
- ```bash
91
+ ```shell
88
92
  pip install --upgrade onvif-python
89
93
  ```
94
+
90
95
  Or clone this repository and install locally:
91
- ```bash
96
+ ```shell
92
97
  git clone https://github.com/nirsimetri/onvif-python
93
98
  cd onvif-python
94
99
  pip install .
95
100
  ```
96
101
 
102
+ Once the installation is successful, you can also immediately access the [ONVIF CLI](https://nirsimetri.github.io/onvif-python/core/onvif_cli/) via the terminal using:
103
+ ```shell
104
+ onvif --help
105
+ ```
106
+
97
107
  ## Documentation
98
108
 
99
109
  For complete guide, including usage examples and API references, please visit the [Documentation Page](https://nirsimetri.github.io/onvif-python/).
@@ -2,7 +2,7 @@
2
2
 
3
3
  <div align="center">
4
4
  <a href="https://app.codacy.com/gh/nirsimetri/onvif-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade"><img src="https://img.shields.io/codacy/grade/bff08a94e4d447b690cea49c6594826d?style=plastic&logo=codacy&label=Code%20Quality"/></a>
5
- <a href="https://pypi.org/project/onvif-python/"><img alt="PyPI Version" src="https://img.shields.io/badge/Version-0.3.1-orange?logo=python&logoColor=white&color=yellow&style=plastic"></a>
5
+ <a href="https://pypi.org/project/onvif-python/"><img alt="PyPI Version" src="https://img.shields.io/badge/Version-0.4.0-orange?logo=python&logoColor=white&color=yellow&style=plastic"></a>
6
6
  <a href="https://pepy.tech/projects/onvif-python"><img alt="Pepy Total Downloads" src="https://img.shields.io/pepy/dt/onvif-python?logo=pypi&logoColor=white&label=Downloads&color=red&style=plastic"></a>
7
7
  <br>
8
8
  <a href="https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml"><img alt="Build" src="https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml/badge.svg?branch=main"></a>
@@ -29,7 +29,7 @@ This library simplifies that process by wrapping SOAP communication into a clean
29
29
 
30
30
  ## Requirements
31
31
 
32
- - **Python**: 3.9 or higher
32
+ - **Python**: 3.10 or higher
33
33
  - **Dependencies**:
34
34
  - [`zeep>=4.3.0`](https://github.com/mvantellingen/python-zeep) - SOAP client for ONVIF communication
35
35
  - [`requests>=2.32.0`](https://github.com/psf/requests) - HTTP library for network requests
@@ -37,16 +37,22 @@ This library simplifies that process by wrapping SOAP communication into a clean
37
37
  ## Installation
38
38
 
39
39
  From official [PyPI](https://pypi.org/project/onvif-python/):
40
- ```bash
40
+ ```shell
41
41
  pip install --upgrade onvif-python
42
42
  ```
43
+
43
44
  Or clone this repository and install locally:
44
- ```bash
45
+ ```shell
45
46
  git clone https://github.com/nirsimetri/onvif-python
46
47
  cd onvif-python
47
48
  pip install .
48
49
  ```
49
50
 
51
+ Once the installation is successful, you can also immediately access the [ONVIF CLI](https://nirsimetri.github.io/onvif-python/core/onvif_cli/) via the terminal using:
52
+ ```shell
53
+ onvif --help
54
+ ```
55
+
50
56
  ## Documentation
51
57
 
52
58
  For complete guide, including usage examples and API references, please visit the [Documentation Page](https://nirsimetri.github.io/onvif-python/).
@@ -6,10 +6,12 @@ from onvif.operator import CacheMode
6
6
  from onvif.utils import (
7
7
  ONVIFWSDL,
8
8
  ONVIFDiscovery,
9
- ONVIFErrorHandler,
10
9
  ONVIFOperationException,
11
10
  ONVIFParser,
12
11
  ZeepPatcher,
12
+ ignore_unsupported,
13
+ is_action_not_supported,
14
+ safe_call,
13
15
  )
14
16
 
15
17
  __all__ = [
@@ -17,9 +19,11 @@ __all__ = [
17
19
  "CacheMode",
18
20
  "ONVIFWSDL",
19
21
  "ONVIFOperationException",
20
- "ONVIFErrorHandler",
21
22
  "ZeepPatcher",
22
23
  "ONVIFCLI",
23
24
  "ONVIFDiscovery",
24
25
  "ONVIFParser",
26
+ "ignore_unsupported",
27
+ "is_action_not_supported",
28
+ "safe_call",
25
29
  ]
@@ -1,5 +1,7 @@
1
1
  """ONVIF discovery helpers."""
2
2
 
3
+ from datetime import datetime
4
+
3
5
  from onvif.cli.utils import colorize
4
6
  from onvif.utils import ONVIFDiscovery
5
7
 
@@ -16,7 +18,7 @@ def discover_devices(
16
18
  timeout (int): Discovery timeout in seconds
17
19
  interface (str | None): Network interface to use for discovery
18
20
  prefer_https (bool): If True, prioritize HTTPS XAddrs when available
19
- filter_term (str | None): Optional search term to filter devices by types or scopes
21
+ filter_term (str | None): Optional search term to filter devices by types or scopes (case-insensitive)
20
22
 
21
23
  Returns:
22
24
  List of discovered devices with connection info
@@ -36,6 +38,7 @@ def discover_devices(
36
38
  return devices
37
39
 
38
40
 
41
+ # pylint: disable=too-many-locals,too-many-statements,too-many-branches
39
42
  def select_device_interactive(devices: list) -> tuple[str, int, bool] | None:
40
43
  """Display devices and allow user to select one interactively.
41
44
 
@@ -70,15 +73,43 @@ def select_device_interactive(devices: list) -> tuple[str, int, bool] | None:
70
73
  epr_display = epr_display.replace("uuid:", "")
71
74
  print(f" [uuid] {epr_display}")
72
75
 
73
- if device["xaddrs"]:
76
+ if device.get("hostname"):
77
+ print(f" [hostname] {device["hostname"]}")
78
+
79
+ if device.get("xaddrs"):
74
80
  xaddrs_parts = [f"[{xaddr}]" for xaddr in device["xaddrs"]]
75
81
  print(f" [xaddrs] {' '.join(xaddrs_parts)}")
76
82
 
77
- if device["types"]:
83
+ if device.get("date_time"):
84
+ utc = (
85
+ datetime.fromisoformat(device["date_time"].get("utc")).strftime(
86
+ "%H:%M:%S %d-%m-%Y"
87
+ )
88
+ if device["date_time"].get("utc")
89
+ else ""
90
+ )
91
+ local = (
92
+ datetime.fromisoformat(device["date_time"].get("local")).strftime(
93
+ "%H:%M:%S %d-%m-%Y"
94
+ )
95
+ if device["date_time"].get("local")
96
+ else ""
97
+ )
98
+ print(
99
+ f" [date_time]"
100
+ f'{" [UTC: " + utc + "]" if utc else ""}'
101
+ f'{" [Local: " + local + "]" if local else ""}'
102
+ )
103
+
104
+ if device.get("types"):
78
105
  types_parts = [f"[{t}]" for t in device["types"]]
79
106
  print(f" [types] {' '.join(types_parts)}")
80
107
 
81
- if device["scopes"]:
108
+ if device.get("services"):
109
+ service = [colorize(f"[{t}]", "cyan") for t in device["services"]]
110
+ print(f" [services] {' '.join(service)}")
111
+
112
+ if device.get("scopes"):
82
113
  scope_parts = []
83
114
  for scope in device["scopes"]:
84
115
  # Remove the prefix "onvif://www.onvif.org/" if present
@@ -110,13 +141,14 @@ def select_device_interactive(devices: list) -> tuple[str, int, bool] | None:
110
141
  print(
111
142
  f"\n{colorize('Selected:', 'green')} {colorize(protocol, 'cyan')}://{colorize(host_port, 'yellow')}"
112
143
  )
144
+
113
145
  return (
114
146
  selected["host"],
115
147
  selected["port"],
116
148
  selected.get("use_https", False),
117
149
  )
118
- else:
119
- print(colorize("Invalid selection. Please try again.", "red"))
150
+
151
+ print(colorize("Invalid selection. Please try again.", "red"))
120
152
 
121
153
  except ValueError:
122
154
  print(colorize("Invalid input. Please enter a number.", "red"))
@@ -102,6 +102,7 @@ def save_output_to_file(
102
102
  print(str(result))
103
103
 
104
104
 
105
+ # pylint: disable=too-many-return-statements,too-many-branches,too-many-nested-blocks
105
106
  def _serialize_for_json(obj: Any) -> Any:
106
107
  """Recursively serialize ONVIF objects for JSON output.
107
108
 
@@ -113,17 +114,17 @@ def _serialize_for_json(obj: Any) -> Any:
113
114
  """
114
115
  if obj is None:
115
116
  return None
116
- elif isinstance(obj, (str, int, float, bool)):
117
+ if isinstance(obj, (str, int, float, bool)):
117
118
  return obj
118
- elif isinstance(obj, datetime):
119
+ if isinstance(obj, datetime):
119
120
  return obj.isoformat()
120
- elif isinstance(obj, (list, tuple)):
121
+ if isinstance(obj, (list, tuple)):
121
122
  return [_serialize_for_json(item) for item in obj]
122
- elif isinstance(obj, dict):
123
+ if isinstance(obj, dict):
123
124
  return {key: _serialize_for_json(value) for key, value in obj.items()}
124
125
 
125
126
  # Check if this is a Zeep object (has _xsd_type attribute)
126
- elif hasattr(obj, "_xsd_type"):
127
+ if hasattr(obj, "_xsd_type"):
127
128
  result = {}
128
129
  # Try to get all elements from XSD type
129
130
  if hasattr(obj._xsd_type, "elements"): # pylint: disable=protected-access
@@ -154,7 +155,7 @@ def _serialize_for_json(obj: Any) -> Any:
154
155
 
155
156
  return result
156
157
 
157
- elif hasattr(obj, "__dict__"):
158
+ if hasattr(obj, "__dict__"):
158
159
  # Handle regular objects with attributes
159
160
  result = {}
160
161
  for key, value in obj.__dict__.items():
@@ -176,14 +177,15 @@ def _serialize_for_json(obj: Any) -> Any:
176
177
  pass
177
178
 
178
179
  return result
179
- elif hasattr(obj, "_value_1"):
180
+
181
+ if hasattr(obj, "_value_1"):
180
182
  # Handle zeep objects with special structure
181
183
  return _serialize_for_json(obj._value_1) # pylint: disable=protected-access
182
- else:
183
- # Try to convert to dict using vars() if available
184
- try:
185
- obj_dict = vars(obj)
186
- return _serialize_for_json(obj_dict)
187
- except TypeError:
188
- # Fallback to string representation
189
- return str(obj)
184
+
185
+ # Try to convert to dict using vars() if available
186
+ try:
187
+ obj_dict = vars(obj)
188
+ return _serialize_for_json(obj_dict)
189
+ except TypeError:
190
+ # Fallback to string representation
191
+ return str(obj)
@@ -69,7 +69,8 @@ def print_interactive_intro(args, device_info_text) -> str:
69
69
  f"{banner}\n"
70
70
  f"{repo_info}\n"
71
71
  f"{terminal_header}\n"
72
- f" Connected to : {colorize(f'{args.host}:{args.port}', 'yellow')}"
72
+ f" Connected to : {colorize(f'{args.host}:{args.port}', 'yellow')}\n"
73
+ f" Auth Method : {colorize(f'{'HTTP Digest' if args.digest else 'WS-UsernameToken'}', 'yellow')}"
73
74
  f"{options_display}{device_info_text}\n\n"
74
75
  f"{colorize('[Quick Start]', 'green')}\n"
75
76
  f" - Type {colorize('dev', 'yellow')} + {colorize('TAB', 'yellow')} to see `devicemgmt` suggestion\n"
@@ -105,7 +106,6 @@ INTERACTIVE_HELP = f"""
105
106
  cd <service> - Enter service mode (alias)
106
107
  ls - List commands/services/methods in grid format
107
108
  up - Exit current service mode (go up one level)
108
- pwd - Show current service context
109
109
  clear - Clear terminal screen
110
110
  help <command> - Show help for a specific command
111
111
 
@@ -165,7 +165,6 @@ INTERACTIVE_SHORTCUTS = f"""
165
165
  cd <service> - Enter service (same as '<service>')
166
166
  ls - List commands/services in grid format (like TAB)
167
167
  up - Go up one level
168
- pwd - Show current context
169
168
  clear - Clear terminal screen
170
169
  help <command> - Show help for a command
171
170
 
@@ -193,7 +192,7 @@ Examples:
193
192
  # Discover ONVIF devices on network
194
193
  {colorize('onvif', 'yellow')} --discover --username admin --password admin123 --interactive
195
194
  {colorize('onvif', 'yellow')} media GetProfiles --discover --username admin
196
- {colorize('onvif', 'yellow')} -d -i
195
+ {colorize('onvif', 'yellow')} -d --interface 192.168.1.77 -i
197
196
 
198
197
  # Discover with filtering
199
198
  {colorize('onvif', 'yellow')} --discover --filter ptz --interactive
@@ -10,6 +10,8 @@ from pathlib import Path
10
10
  from onvif.cli.utils import colorize
11
11
 
12
12
 
13
+ # pylint: disable=too-many-locals,too-many-nested-blocks
14
+ # pylint: disable=too-many-statements,too-many-branches
13
15
  def search_products(search_term: str, page: int = 1, per_page: int = 20) -> None:
14
16
  """Search ONVIF products database and display results in table format with
15
17
  pagination.
@@ -36,6 +36,7 @@ class OperationTypeInfo(TypedDict):
36
36
  output: MessageInfo | None
37
37
 
38
38
 
39
+ # pylint: disable=too-many-locals,too-many-statements,too-many-branches
39
40
  def get_method_documentation(service_obj, method_name: str) -> dict[str, Any] | None:
40
41
  """Extracts documentation from WSDL and parameters from the Python method signature.
41
42
 
@@ -45,7 +46,7 @@ def get_method_documentation(service_obj, method_name: str) -> dict[str, Any] |
45
46
  required_args: list[str] = []
46
47
  optional_args: list[str] = []
47
48
 
48
- try:
49
+ try: # pylint: disable=too-many-nested-blocks
49
50
  # 1. Get documentation from WSDL (existing logic)
50
51
  wsdl_path = service_obj.operator.wsdl_path
51
52
 
@@ -308,8 +309,7 @@ def clean_documentation_html(doc_text: str) -> str:
308
309
  text = match.group(2).strip() # Strip whitespace from link text
309
310
  if text:
310
311
  return f"{text} ({url})"
311
- else:
312
- return url # If no text, just return URL
312
+ return url # If no text, just return URL
313
313
 
314
314
  # Replace anchor tags - handle newlines and whitespace
315
315
  doc_text = re.sub(
@@ -361,7 +361,9 @@ def extract_documentation_text(doc_elem) -> str:
361
361
  def _load_imported_schemas(root, schema_context: dict, namespaces: dict):
362
362
  """Recursively load all imported and included schemas into the schema context."""
363
363
  # Find all xs:import and xs:include in xs:schema elements
364
- for schema in root.findall(".//xs:schema", namespaces):
364
+ for schema in root.findall( # pylint: disable=too-many-nested-blocks
365
+ ".//xs:schema", namespaces
366
+ ):
365
367
  # Process imports
366
368
  for import_elem in schema.findall("xs:import", namespaces):
367
369
  schema_location = import_elem.get("schemaLocation")
@@ -443,6 +445,7 @@ def _load_imported_schemas(root, schema_context: dict, namespaces: dict):
443
445
  continue
444
446
 
445
447
 
448
+ # pylint: disable=too-many-locals,too-many-statements,too-many-branches
446
449
  def resolve_element_type(
447
450
  element_name: str,
448
451
  namespaces: dict,
@@ -605,6 +608,7 @@ def resolve_element_type(
605
608
  return parameters
606
609
 
607
610
 
611
+ # pylint: disable=too-many-locals,too-many-statements,too-many-branches
608
612
  def resolve_complex_type(
609
613
  type_name: str,
610
614
  namespaces: dict,
@@ -794,6 +798,7 @@ def resolve_complex_type(
794
798
  return children
795
799
 
796
800
 
801
+ # pylint: disable=too-many-locals,too-many-statements,too-many-branches
797
802
  def parse_inline_complex_type(
798
803
  complex_type,
799
804
  namespaces: dict,
@@ -1,3 +1,5 @@
1
+ # pylint: disable=too-many-lines,too-many-instance-attributes,too-many-locals
2
+ # pylint: disable=too-many-statements,too-many-public-methods,too-many-branches
1
3
  """ONVIF CLI interactive shell implementation."""
2
4
 
3
5
  import cmd
@@ -70,7 +72,6 @@ class InteractiveShell(cmd.Cmd):
70
72
  "debug",
71
73
  "ls",
72
74
  "cd",
73
- "pwd",
74
75
  "shortcuts",
75
76
  "desc",
76
77
  "type",
@@ -274,6 +275,7 @@ class InteractiveShell(cmd.Cmd):
274
275
  depth = max(0, depth - 1)
275
276
 
276
277
  # Detect top-level &&
278
+ # pylint: disable=too-many-boolean-expressions
277
279
  if (
278
280
  not in_single
279
281
  and not in_double
@@ -522,6 +524,8 @@ class InteractiveShell(cmd.Cmd):
522
524
  print(f"{colorize('Unknown command:', 'red')} {line}")
523
525
  print(f"Type {colorize('help', 'white')} for available commands")
524
526
 
527
+ return None
528
+
525
529
  def get_suggestions(self, partial_cmd: str) -> list[str]:
526
530
  """Get command suggestions based on partial input."""
527
531
  suggestions = []
@@ -1026,7 +1030,7 @@ class InteractiveShell(cmd.Cmd):
1026
1030
  available_services = get_device_available_services(self.client)
1027
1031
  colored_services = [colorize(svc, "cyan") for svc in available_services]
1028
1032
  print(f"Available services: {', '.join(colored_services)}")
1029
- return
1033
+ return None
1030
1034
  return self.do_enter_service(line)
1031
1035
 
1032
1036
  def complete_cd(
@@ -1036,17 +1040,6 @@ class InteractiveShell(cmd.Cmd):
1036
1040
  services = get_device_available_services(self.client)
1037
1041
  return [s for s in services if s.lower().startswith(text.lower())]
1038
1042
 
1039
- def do_pwd(self, line): # pylint: disable=unused-argument
1040
- """Show current service context."""
1041
- if self.current_service_name:
1042
- print(
1043
- f"{colorize('Current service:', 'yellow')} {colorize(self.current_service_name, 'cyan')}"
1044
- )
1045
- else:
1046
- print(
1047
- f"{colorize('Current context:', 'yellow')} {colorize('root', 'blue')}"
1048
- )
1049
-
1050
1043
  def do_shortcuts(self, line): # pylint: disable=unused-argument
1051
1044
  """Show available shortcuts."""
1052
1045
  print(INTERACTIVE_SHORTCUTS)
@@ -1221,6 +1214,7 @@ class InteractiveShell(cmd.Cmd):
1221
1214
  print(
1222
1215
  f"\n{colorize('[ONVIF Terminal Client]', 'yellow')}"
1223
1216
  f"\n Connected to : {colorize(f'{self.args.host}:{self.args.port}', 'yellow')}"
1217
+ f"\n Auth Method : {colorize(f'{'HTTP Digest' if self.args.digest else 'WS-UsernameToken'}', 'yellow')}"
1224
1218
  f"{options_display}{self.device_info_text}"
1225
1219
  )
1226
1220
  print() # Extra newline for spacing
@@ -24,7 +24,6 @@ from onvif.operator import CacheMode
24
24
  from onvif.utils import ONVIFOperationException
25
25
 
26
26
 
27
- # pylint: disable=line-too-long
28
27
  def create_parser() -> ArgumentParser:
29
28
  """Create argument parser for ONVIF CLI."""
30
29
  parser = ArgumentParser(
@@ -97,6 +96,11 @@ def create_parser() -> ArgumentParser:
97
96
  default=10,
98
97
  help="ONVIF connection timeout in seconds (default: 10)",
99
98
  )
99
+ parser.add_argument(
100
+ "--digest",
101
+ action="store_true",
102
+ help="Use HTTP Digest instead of WS-UsernameToken",
103
+ )
100
104
  parser.add_argument(
101
105
  "--https", action="store_true", help="Use HTTPS instead of HTTP"
102
106
  )
@@ -118,9 +122,9 @@ def create_parser() -> ArgumentParser:
118
122
  parser.add_argument(
119
123
  "--cache",
120
124
  choices=[mode.value for mode in CacheMode],
121
- default=CacheMode.ALL.value,
122
- help="Caching mode for ONVIFClient (default: all). "
123
- "'all': memory+disk, 'db': disk-only, 'mem': memory-only, 'none': disabled.",
125
+ default=CacheMode.DB.value,
126
+ help="Caching mode for ONVIFClient (default: db). "
127
+ "'db': disk-only, 'mem': memory-only, 'none': disabled.",
124
128
  )
125
129
  parser.add_argument(
126
130
  "--health-check-interval",
@@ -159,6 +163,7 @@ def create_parser() -> ArgumentParser:
159
163
  return parser
160
164
 
161
165
 
166
+ # pylint: disable=too-many-statements,too-many-branches
162
167
  def main() -> None:
163
168
  """Main CLI entry point."""
164
169
  # Setup custom warning format for cleaner output
@@ -278,6 +283,7 @@ def main() -> None:
278
283
  port=args.port,
279
284
  username=args.username,
280
285
  password=args.password,
286
+ http_digest=args.digest,
281
287
  timeout=args.timeout,
282
288
  cache=CacheMode(args.cache),
283
289
  use_https=args.https,
@@ -80,6 +80,7 @@ def _is_valid_json(s: str) -> bool:
80
80
  return True
81
81
 
82
82
 
83
+ # pylint: disable=too-many-statements,too-many-branches
83
84
  def parse_json_params(params_str: str) -> dict[str, Any]:
84
85
  """Parse parameters from a JSON string or key=value pairs into a dict.
85
86
 
@@ -406,6 +407,7 @@ def format_services_list(services_list) -> str:
406
407
  return result
407
408
 
408
409
 
410
+ # pylint: disable=too-many-statements,too-many-branches
409
411
  def get_device_available_services(client) -> list:
410
412
  """Get list of services actually available on the connected device.
411
413