onvif-python 0.1.6__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 (131) hide show
  1. {onvif_python-0.1.6/onvif_python.egg-info → onvif_python-0.1.8}/PKG-INFO +40 -11
  2. {onvif_python-0.1.6 → onvif_python-0.1.8}/README.md +32 -8
  3. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/cli/interactive.py +2 -2
  4. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/cli/main.py +34 -20
  5. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/cli/utils.py +38 -13
  6. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/client.py +101 -21
  7. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/operator.py +50 -0
  8. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/accesscontrol.py +13 -2
  9. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/accessrules.py +2 -2
  10. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/actionengine.py +2 -2
  11. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/analytics/analytics.py +2 -2
  12. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/analytics/ruleengine.py +2 -2
  13. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/analyticsdevice.py +2 -2
  14. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/appmgmt.py +2 -2
  15. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/authenticationbehavior.py +2 -2
  16. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/credential.py +2 -2
  17. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/deviceio.py +4 -4
  18. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/devicemgmt.py +4 -4
  19. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/display.py +2 -2
  20. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/doorcontrol.py +2 -2
  21. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/events/events.py +2 -2
  22. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/events/notification.py +2 -2
  23. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/events/pullpoint.py +2 -2
  24. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/events/subscription.py +2 -2
  25. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/imaging.py +2 -2
  26. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/media.py +2 -2
  27. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/media2.py +2 -2
  28. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/provisioning.py +2 -2
  29. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/ptz.py +2 -2
  30. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/receiver.py +2 -2
  31. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/recording.py +2 -2
  32. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/replay.py +2 -2
  33. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/schedule.py +2 -2
  34. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/search.py +2 -2
  35. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/security/advancedsecurity.py +2 -2
  36. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/security/authorizationserver.py +2 -2
  37. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/security/dot1x.py +2 -2
  38. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/security/jwt.py +2 -2
  39. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/security/keystore.py +2 -2
  40. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/security/mediasigning.py +2 -2
  41. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/security/tlsserver.py +3 -3
  42. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/thermal.py +2 -2
  43. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/uplink.py +2 -2
  44. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/utils/__init__.py +2 -0
  45. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/utils/discovery.py +96 -22
  46. {onvif_python-0.1.6 → 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.6 → onvif_python-0.1.8}/onvif/utils/wsdl.py +144 -16
  50. onvif_python-0.1.8/onvif/utils/xml_capture.py +195 -0
  51. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/utils/zeep.py +144 -20
  52. {onvif_python-0.1.6 → onvif_python-0.1.8/onvif_python.egg-info}/PKG-INFO +40 -11
  53. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif_python.egg-info/SOURCES.txt +1 -0
  54. {onvif_python-0.1.6 → onvif_python-0.1.8}/pyproject.toml +8 -3
  55. {onvif_python-0.1.6 → onvif_python-0.1.8}/tests/test_client.py +3 -1
  56. onvif_python-0.1.6/onvif/utils/exceptions.py +0 -91
  57. onvif_python-0.1.6/onvif/utils/xml_capture.py +0 -145
  58. {onvif_python-0.1.6 → onvif_python-0.1.8}/LICENSE.md +0 -0
  59. {onvif_python-0.1.6 → onvif_python-0.1.8}/MANIFEST.in +0 -0
  60. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/__init__.py +0 -0
  61. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/cli/__init__.py +0 -0
  62. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/cli/__main__.py +0 -0
  63. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/__init__.py +0 -0
  64. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
  65. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
  66. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
  67. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
  68. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
  69. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
  70. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
  71. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
  72. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
  73. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
  74. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
  75. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
  76. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
  77. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
  78. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
  79. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/display/display.wsdl +0 -0
  80. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
  81. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/display.wsdl +0 -0
  82. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
  83. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
  84. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
  85. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
  86. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
  87. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
  88. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
  89. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
  90. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
  91. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
  92. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
  93. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/receiver.wsdl +0 -0
  94. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/recording.wsdl +0 -0
  95. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/replay.wsdl +0 -0
  96. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
  97. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
  98. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/schema/common.xsd +0 -0
  99. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
  100. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
  101. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/search.wsdl +0 -0
  102. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
  103. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
  104. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
  105. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
  106. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
  107. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
  108. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
  109. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/index.html +0 -0
  110. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
  111. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
  112. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
  113. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
  114. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
  115. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
  116. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
  117. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
  118. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
  119. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
  120. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
  121. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
  122. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif_python.egg-info/dependency_links.txt +0 -0
  123. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif_python.egg-info/entry_points.txt +0 -0
  124. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif_python.egg-info/requires.txt +0 -0
  125. {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif_python.egg-info/top_level.txt +0 -0
  126. {onvif_python-0.1.6 → onvif_python-0.1.8}/setup.cfg +0 -0
  127. {onvif_python-0.1.6 → onvif_python-0.1.8}/setup.py +0 -0
  128. {onvif_python-0.1.6 → onvif_python-0.1.8}/tests/test_core.py +0 -0
  129. {onvif_python-0.1.6 → onvif_python-0.1.8}/tests/test_error_handlers.py +0 -0
  130. {onvif_python-0.1.6 → onvif_python-0.1.8}/tests/test_exceptions.py +0 -0
  131. {onvif_python-0.1.6 → onvif_python-0.1.8}/tests/test_wsdl_map.py +0 -0
@@ -1,17 +1,22 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onvif-python
3
- Version: 0.1.6
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
8
+ Project-URL: Homepage, https://github.com/nirsimetri/onvif-python
8
9
  Project-URL: Documentation, https://deepwiki.com/nirsimetri/onvif-python
9
10
  Project-URL: Changelog, https://github.com/nirsimetri/onvif-python/releases
10
11
  Keywords: onvif,Camera,onvif-python,ip-camera,cctv,soap,surveillance,cctv cameras,soap-client,onvif-client,onvif-library,onvif-specifications
11
12
  Classifier: Development Status :: 5 - Production/Stable
12
13
  Classifier: Environment :: Console
13
- Classifier: Programming Language :: Python :: 3
14
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
15
20
  Classifier: Operating System :: OS Independent
16
21
  Classifier: Intended Audience :: Developers
17
22
  Classifier: Intended Audience :: Information Technology
@@ -36,15 +41,23 @@ Requires-Dist: black; extra == "dev"
36
41
  Requires-Dist: flake8; extra == "dev"
37
42
  Dynamic: license-file
38
43
 
39
- # ONVIF Python
44
+ <h1 align="center">ONVIF Python</h1>
40
45
 
41
- [![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)
42
49
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/nirsimetri/onvif-python)
43
- [![PyPI](https://img.shields.io/badge/PyPI-0.1.6-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/)
44
51
  [![Downloads](https://img.shields.io/pypi/dm/onvif-python?label=Downloads&color=red)](https://clickpy.clickhouse.com/dashboard/onvif-python)
45
52
  <br>
46
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)
47
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>
48
61
 
49
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.
50
63
 
@@ -107,6 +120,10 @@ discovery = ONVIFDiscovery(timeout=5)
107
120
  # Discover devices
108
121
  devices = discovery.discover()
109
122
 
123
+ # Or with
124
+ # Discover with search filter by types or scopes (case-insensitive substring match)
125
+ devices = discovery.discover(search="Profile/Streaming")
126
+
110
127
  # Display discovered devices
111
128
  for device in devices:
112
129
  print(f"Found device at {device['host']}:{device['port']}")
@@ -182,7 +199,7 @@ Explore more advanced usage and service-specific operations in the [`examples/`]
182
199
  ## ONVIF CLI
183
200
 
184
201
  > [!NOTE]
185
- > 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).
186
203
 
187
204
  ![Windows](https://img.shields.io/badge/Windows-0078D6?style=plastic&logo=gitforwindows&logoColor=white)
188
205
  ![Linux](https://img.shields.io/badge/Linux-FCC624?style=plastic&logo=linux&logoColor=black)
@@ -232,11 +249,12 @@ This library includes a powerful command-line interface (CLI) for interacting wi
232
249
  <summary><b>1. Direct CLI</b></summary>
233
250
 
234
251
  ```bash
235
- usage: onvif [-h] [--host HOST] [--port PORT] [--username USERNAME] [--password PASSWORD] [--discover] [--timeout TIMEOUT] [--https] [--no-verify]
236
- [--no-patch] [--interactive] [--debug] [--wsdl WSDL] [--cache {all,db,mem,none}] [--health-check-interval HEALTH_CHECK_INTERVAL] [--version]
252
+ usage: onvif [-h] [--host HOST] [--port PORT] [--username USERNAME] [--password PASSWORD] [--discover] [--search SEARCH] [--timeout TIMEOUT] [--https]
253
+ [--no-verify] [--no-patch] [--interactive] [--debug] [--wsdl WSDL] [--cache {all,db,mem,none}]
254
+ [--health-check-interval HEALTH_CHECK_INTERVAL] [--version]
237
255
  [service] [method] [params ...]
238
256
 
239
- ONVIF Terminal Client — v0.1.6
257
+ ONVIF Terminal Client — v0.1.8
240
258
  https://github.com/nirsimetri/onvif-python
241
259
 
242
260
  positional arguments:
@@ -253,6 +271,8 @@ options:
253
271
  --password PASSWORD, -p PASSWORD
254
272
  Password for authentication
255
273
  --discover, -d Discover ONVIF devices on the network using WS-Discovery
274
+ --search SEARCH, -s SEARCH
275
+ Filter discovered devices by types or scopes (case-insensitive substring match)
256
276
  --timeout TIMEOUT Connection timeout in seconds (default: 10)
257
277
  --https Use HTTPS instead of HTTP
258
278
  --no-verify Disable SSL certificate verification
@@ -272,6 +292,11 @@ Examples:
272
292
  onvif media GetProfiles --discover --username admin
273
293
  onvif -d -i
274
294
 
295
+ # Discover with filtering
296
+ onvif --discover --search ptz --interactive
297
+ onvif -d -s "C210" -i
298
+ onvif -d -s "audio_encoder" -u admin -p admin123 -i
299
+
275
300
  # Direct command execution
276
301
  onvif devicemgmt GetCapabilities Category=All --host 192.168.1.17 --port 8000 --username admin --password admin123
277
302
  onvif ptz ContinuousMove ProfileToken=Profile_1 Velocity={"PanTilt": {"x": -0.1, "y": 0}} --host 192.168.1.17 --port 8000 --username admin --password admin123
@@ -293,7 +318,7 @@ Examples:
293
318
  <summary><b>2. Interactive Shell</b></summary>
294
319
 
295
320
  ```bash
296
- ONVIF Interactive Shell Commands - v0.1.6
321
+ ONVIF Interactive Shell — v0.1.8
297
322
  https://github.com/nirsimetri/onvif-python
298
323
 
299
324
  Basic Commands:
@@ -426,6 +451,10 @@ onvif --discover --username admin --password admin123 --interactive
426
451
  # Short form
427
452
  onvif -d -u admin -p admin123 -i
428
453
 
454
+ # Discover with search filter
455
+ onvif --discover --search "C210" --interactive
456
+ onvif -d -s ptz -u admin -p admin123 -i
457
+
429
458
  # Discover and interactive (will prompt for credentials)
430
459
  onvif -d -i
431
460
  ```
@@ -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.6-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
 
@@ -69,6 +77,10 @@ discovery = ONVIFDiscovery(timeout=5)
69
77
  # Discover devices
70
78
  devices = discovery.discover()
71
79
 
80
+ # Or with
81
+ # Discover with search filter by types or scopes (case-insensitive substring match)
82
+ devices = discovery.discover(search="Profile/Streaming")
83
+
72
84
  # Display discovered devices
73
85
  for device in devices:
74
86
  print(f"Found device at {device['host']}:{device['port']}")
@@ -144,7 +156,7 @@ Explore more advanced usage and service-specific operations in the [`examples/`]
144
156
  ## ONVIF CLI
145
157
 
146
158
  > [!NOTE]
147
- > 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).
148
160
 
149
161
  ![Windows](https://img.shields.io/badge/Windows-0078D6?style=plastic&logo=gitforwindows&logoColor=white)
150
162
  ![Linux](https://img.shields.io/badge/Linux-FCC624?style=plastic&logo=linux&logoColor=black)
@@ -194,11 +206,12 @@ This library includes a powerful command-line interface (CLI) for interacting wi
194
206
  <summary><b>1. Direct CLI</b></summary>
195
207
 
196
208
  ```bash
197
- usage: onvif [-h] [--host HOST] [--port PORT] [--username USERNAME] [--password PASSWORD] [--discover] [--timeout TIMEOUT] [--https] [--no-verify]
198
- [--no-patch] [--interactive] [--debug] [--wsdl WSDL] [--cache {all,db,mem,none}] [--health-check-interval HEALTH_CHECK_INTERVAL] [--version]
209
+ usage: onvif [-h] [--host HOST] [--port PORT] [--username USERNAME] [--password PASSWORD] [--discover] [--search SEARCH] [--timeout TIMEOUT] [--https]
210
+ [--no-verify] [--no-patch] [--interactive] [--debug] [--wsdl WSDL] [--cache {all,db,mem,none}]
211
+ [--health-check-interval HEALTH_CHECK_INTERVAL] [--version]
199
212
  [service] [method] [params ...]
200
213
 
201
- ONVIF Terminal Client — v0.1.6
214
+ ONVIF Terminal Client — v0.1.8
202
215
  https://github.com/nirsimetri/onvif-python
203
216
 
204
217
  positional arguments:
@@ -215,6 +228,8 @@ options:
215
228
  --password PASSWORD, -p PASSWORD
216
229
  Password for authentication
217
230
  --discover, -d Discover ONVIF devices on the network using WS-Discovery
231
+ --search SEARCH, -s SEARCH
232
+ Filter discovered devices by types or scopes (case-insensitive substring match)
218
233
  --timeout TIMEOUT Connection timeout in seconds (default: 10)
219
234
  --https Use HTTPS instead of HTTP
220
235
  --no-verify Disable SSL certificate verification
@@ -234,6 +249,11 @@ Examples:
234
249
  onvif media GetProfiles --discover --username admin
235
250
  onvif -d -i
236
251
 
252
+ # Discover with filtering
253
+ onvif --discover --search ptz --interactive
254
+ onvif -d -s "C210" -i
255
+ onvif -d -s "audio_encoder" -u admin -p admin123 -i
256
+
237
257
  # Direct command execution
238
258
  onvif devicemgmt GetCapabilities Category=All --host 192.168.1.17 --port 8000 --username admin --password admin123
239
259
  onvif ptz ContinuousMove ProfileToken=Profile_1 Velocity={"PanTilt": {"x": -0.1, "y": 0}} --host 192.168.1.17 --port 8000 --username admin --password admin123
@@ -255,7 +275,7 @@ Examples:
255
275
  <summary><b>2. Interactive Shell</b></summary>
256
276
 
257
277
  ```bash
258
- ONVIF Interactive Shell Commands - v0.1.6
278
+ ONVIF Interactive Shell — v0.1.8
259
279
  https://github.com/nirsimetri/onvif-python
260
280
 
261
281
  Basic Commands:
@@ -388,6 +408,10 @@ onvif --discover --username admin --password admin123 --interactive
388
408
  # Short form
389
409
  onvif -d -u admin -p admin123 -i
390
410
 
411
+ # Discover with search filter
412
+ onvif --discover --search "C210" --interactive
413
+ onvif -d -s ptz -u admin -p admin123 -i
414
+
391
415
  # Discover and interactive (will prompt for credentials)
392
416
  onvif -d -i
393
417
  ```
@@ -191,7 +191,7 @@ class InteractiveShell(cmd.Cmd):
191
191
  " / __ \\/ | / / | / / _/ ____/",
192
192
  " / / / / |/ /| | / // // /_ ",
193
193
  "/ /_/ / /| / | |/ // // __/ ",
194
- "\\____/_/ |_/ |___/___/_/ v0.1.6",
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 Commands - v0.1.6', '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.6\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:
@@ -26,6 +26,11 @@ Examples:
26
26
  {colorize('onvif', 'yellow')} media GetProfiles --discover --username admin
27
27
  {colorize('onvif', 'yellow')} -d -i
28
28
 
29
+ # Discover with filtering
30
+ {colorize('onvif', 'yellow')} --discover --search ptz --interactive
31
+ {colorize('onvif', 'yellow')} -d -s "C210" -i
32
+ {colorize('onvif', 'yellow')} -d -s "audio_encoder" -u admin -p admin123 -i
33
+
29
34
  # Direct command execution
30
35
  {colorize('onvif', 'yellow')} devicemgmt GetCapabilities Category=All --host 192.168.1.17 --port 8000 --username admin --password admin123
31
36
  {colorize('onvif', 'yellow')} ptz ContinuousMove ProfileToken=Profile_1 Velocity={{"PanTilt": {{"x": -0.1, "y": 0}}}} --host 192.168.1.17 --port 8000 --username admin --password admin123
@@ -61,6 +66,11 @@ Examples:
61
66
  action="store_true",
62
67
  help="Discover ONVIF devices on the network using WS-Discovery",
63
68
  )
69
+ parser.add_argument(
70
+ "--search",
71
+ "-s",
72
+ help="Filter discovered devices by types or scopes (case-insensitive substring match)",
73
+ )
64
74
 
65
75
  # Connection options
66
76
  parser.add_argument(
@@ -139,7 +149,7 @@ def main():
139
149
 
140
150
  # Show ONVIF CLI version
141
151
  if args.version:
142
- print(colorize("0.1.6", "yellow"))
152
+ print(colorize("0.1.8", "yellow"))
143
153
  sys.exit(0)
144
154
 
145
155
  # Validate arguments early (before discovery)
@@ -155,11 +165,18 @@ def main():
155
165
  f"{colorize('--discover', 'white')} cannot be used with {colorize('--host', 'white')}"
156
166
  )
157
167
 
158
- # Discover devices (pass --https flag to prioritize HTTPS XAddrs)
159
- devices = discover_devices(timeout=4, prefer_https=args.https)
168
+ # Discover devices (pass --https flag to prioritize HTTPS XAddrs and search term)
169
+ devices = discover_devices(
170
+ timeout=4, prefer_https=args.https, search=args.search
171
+ )
160
172
 
161
173
  if not devices:
162
- print(colorize("No ONVIF devices discovered. Exiting.", "red"))
174
+ if args.search:
175
+ print(
176
+ f"{colorize('No devices found matching search:', 'red')} {colorize(args.search, 'white')}"
177
+ )
178
+ else:
179
+ print(colorize("No ONVIF devices discovered. Exiting.", "red"))
163
180
  sys.exit(1)
164
181
 
165
182
  # Let user select a device
@@ -278,34 +295,31 @@ def execute_command(
278
295
  return method(**params)
279
296
 
280
297
 
281
- def discover_devices(timeout: int = 4, prefer_https: bool = False) -> list:
298
+ def discover_devices(
299
+ timeout: int = 4, prefer_https: bool = False, search: Optional[str] = None
300
+ ) -> list:
282
301
  """Discover ONVIF devices on the network using WS-Discovery.
283
302
 
284
303
  Args:
285
304
  timeout: Discovery timeout in seconds
286
305
  prefer_https: If True, prioritize HTTPS XAddrs when available
306
+ search: Optional search term to filter devices by types or scopes
287
307
 
288
308
  Returns:
289
309
  List of discovered devices with connection info
290
310
  """
291
- # Get local network interface for display
292
- try:
293
- import socket
294
311
 
295
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
296
- s.connect(("8.8.8.8", 80))
297
- local_ip = s.getsockname()[0]
298
- s.close()
299
- except Exception:
300
- local_ip = "0.0.0.0"
312
+ # Use ONVIFDiscovery class
313
+ discovery = ONVIFDiscovery(timeout=timeout)
301
314
 
302
315
  print(f"\n{colorize('Discovering ONVIF devices on network...', 'yellow')}")
303
- print(f"Network interface: {colorize(local_ip, 'white')}")
304
- print(f"Timeout: {timeout}s\n")
316
+ print(f"Network interface: {colorize(discovery._get_local_ip(), 'white')}")
317
+ print(f"Timeout: {timeout}s")
318
+ if search:
319
+ print(f"Filter: {colorize(search, 'yellow')}")
320
+ print()
305
321
 
306
- # Use ONVIFDiscovery class
307
- discovery = ONVIFDiscovery(timeout=timeout)
308
- devices = discovery.discover(prefer_https=prefer_https)
322
+ devices = discovery.discover(prefer_https=prefer_https, search=search)
309
323
 
310
324
  return devices
311
325
 
@@ -4,7 +4,7 @@ import json
4
4
  import os
5
5
  import inspect
6
6
  from typing import Any, Dict, Optional
7
- import xml.etree.ElementTree as ET
7
+ from lxml import etree
8
8
  import re
9
9
 
10
10
 
@@ -231,10 +231,17 @@ def get_method_documentation(service_obj, method_name: str) -> Optional[Dict[str
231
231
  try:
232
232
  # 1. Get documentation from WSDL (existing logic)
233
233
  wsdl_path = service_obj.operator.wsdl_path
234
- tree = ET.parse(wsdl_path)
234
+
235
+ # Use secure lxml parser
236
+ parser = etree.XMLParser(
237
+ resolve_entities=False,
238
+ no_network=True,
239
+ remove_blank_text=True,
240
+ )
241
+ tree = etree.parse(wsdl_path, parser)
235
242
  root = tree.getroot()
236
243
  namespaces = {
237
- node[0]: node[1] for node in ET.iterparse(wsdl_path, events=["start-ns"])
244
+ node[0]: node[1] for node in etree.iterparse(wsdl_path, events=["start-ns"])
238
245
  }
239
246
  namespaces["wsdl"] = "http://schemas.xmlsoap.org/wsdl/"
240
247
  namespaces["xs"] = "http://www.w3.org/2001/XMLSchema"
@@ -310,7 +317,8 @@ def get_method_documentation(service_obj, method_name: str) -> Optional[Dict[str
310
317
  doc_text = colorize("No description available.", "reset")
311
318
 
312
319
  # 2. Get parameters from Python method signature using inspect
313
- 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)
314
322
  sig = inspect.signature(method)
315
323
  for param in sig.parameters.values():
316
324
  if param.name != "self":
@@ -321,10 +329,11 @@ def get_method_documentation(service_obj, method_name: str) -> Optional[Dict[str
321
329
 
322
330
  return {"doc": doc_text, "required": required_args, "optional": optional_args}
323
331
 
324
- except (ET.ParseError, FileNotFoundError, AttributeError, ValueError):
332
+ except (etree.ParseError, FileNotFoundError, AttributeError, ValueError):
325
333
  # Fallback in case of any error, still try to get params
326
334
  try:
327
- 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)
328
337
  sig = inspect.signature(method)
329
338
  for param in sig.parameters.values():
330
339
  if param.name != "self":
@@ -709,14 +718,19 @@ def get_operation_type_info(
709
718
  Returns a dictionary with 'input' and 'output' keys containing message details.
710
719
  """
711
720
  try:
712
- import os
713
-
714
721
  wsdl_path = service_obj.operator.wsdl_path
715
- tree = ET.parse(wsdl_path)
722
+
723
+ # Use secure lxml parser
724
+ parser = etree.XMLParser(
725
+ resolve_entities=False,
726
+ no_network=True,
727
+ remove_blank_text=True,
728
+ )
729
+ tree = etree.parse(wsdl_path, parser)
716
730
  root = tree.getroot()
717
731
 
718
732
  namespaces = {
719
- node[0]: node[1] for node in ET.iterparse(wsdl_path, events=["start-ns"])
733
+ node[0]: node[1] for node in etree.iterparse(wsdl_path, events=["start-ns"])
720
734
  }
721
735
  namespaces["wsdl"] = "http://schemas.xmlsoap.org/wsdl/"
722
736
  namespaces["xs"] = "http://www.w3.org/2001/XMLSchema"
@@ -773,7 +787,6 @@ def _load_imported_schemas(root, schema_context: dict, namespaces: dict):
773
787
  """
774
788
  Recursively load all imported and included schemas into the schema context.
775
789
  """
776
- import os
777
790
 
778
791
  # Find all xs:import and xs:include in xs:schema elements
779
792
  for schema in root.findall(".//xs:schema", namespaces):
@@ -787,7 +800,13 @@ def _load_imported_schemas(root, schema_context: dict, namespaces: dict):
787
800
 
788
801
  if os.path.exists(schema_path):
789
802
  try:
790
- imported_tree = ET.parse(schema_path)
803
+ # Use secure parser for imported schemas
804
+ parser = etree.XMLParser(
805
+ resolve_entities=False,
806
+ no_network=True,
807
+ remove_blank_text=True,
808
+ )
809
+ imported_tree = etree.parse(schema_path, parser)
791
810
  imported_root = imported_tree.getroot()
792
811
 
793
812
  # Add to context if not already present
@@ -817,7 +836,13 @@ def _load_imported_schemas(root, schema_context: dict, namespaces: dict):
817
836
 
818
837
  if os.path.exists(schema_path):
819
838
  try:
820
- included_tree = ET.parse(schema_path)
839
+ # Use secure parser for included schemas
840
+ parser = etree.XMLParser(
841
+ resolve_entities=False,
842
+ no_network=True,
843
+ remove_blank_text=True,
844
+ )
845
+ included_tree = etree.parse(schema_path, parser)
821
846
  included_root = included_tree.getroot()
822
847
 
823
848
  # Add to context if not already present