onvif-python 0.1.5__tar.gz → 0.1.7__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 (128) hide show
  1. {onvif_python-0.1.5/onvif_python.egg-info → onvif_python-0.1.7}/PKG-INFO +49 -11
  2. {onvif_python-0.1.5 → onvif_python-0.1.7}/README.md +47 -10
  3. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/__init__.py +8 -1
  4. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/cli/interactive.py +23 -32
  5. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/cli/main.py +35 -171
  6. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/cli/utils.py +34 -11
  7. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/accesscontrol.py +11 -0
  8. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/security/tlsserver.py +1 -1
  9. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/utils/__init__.py +2 -0
  10. onvif_python-0.1.7/onvif/utils/discovery.py +352 -0
  11. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/utils/xml_capture.py +18 -16
  12. {onvif_python-0.1.5 → onvif_python-0.1.7/onvif_python.egg-info}/PKG-INFO +49 -11
  13. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif_python.egg-info/SOURCES.txt +1 -0
  14. {onvif_python-0.1.5 → onvif_python-0.1.7}/pyproject.toml +2 -1
  15. {onvif_python-0.1.5 → onvif_python-0.1.7}/LICENSE.md +0 -0
  16. {onvif_python-0.1.5 → onvif_python-0.1.7}/MANIFEST.in +0 -0
  17. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/cli/__init__.py +0 -0
  18. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/cli/__main__.py +0 -0
  19. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/client.py +0 -0
  20. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/operator.py +0 -0
  21. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/__init__.py +0 -0
  22. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/accessrules.py +0 -0
  23. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/actionengine.py +0 -0
  24. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/analytics/analytics.py +0 -0
  25. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/analytics/ruleengine.py +0 -0
  26. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/analyticsdevice.py +0 -0
  27. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/appmgmt.py +0 -0
  28. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/authenticationbehavior.py +0 -0
  29. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/credential.py +0 -0
  30. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/deviceio.py +0 -0
  31. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/devicemgmt.py +0 -0
  32. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/display.py +0 -0
  33. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/doorcontrol.py +0 -0
  34. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/events/events.py +0 -0
  35. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/events/notification.py +0 -0
  36. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/events/pullpoint.py +0 -0
  37. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/events/subscription.py +0 -0
  38. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/imaging.py +0 -0
  39. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/media.py +0 -0
  40. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/media2.py +0 -0
  41. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/provisioning.py +0 -0
  42. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/ptz.py +0 -0
  43. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/receiver.py +0 -0
  44. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/recording.py +0 -0
  45. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/replay.py +0 -0
  46. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/schedule.py +0 -0
  47. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/search.py +0 -0
  48. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/security/advancedsecurity.py +0 -0
  49. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/security/authorizationserver.py +0 -0
  50. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/security/dot1x.py +0 -0
  51. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/security/jwt.py +0 -0
  52. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/security/keystore.py +0 -0
  53. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/security/mediasigning.py +0 -0
  54. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/thermal.py +0 -0
  55. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/services/uplink.py +0 -0
  56. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/utils/error_handlers.py +0 -0
  57. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/utils/exceptions.py +0 -0
  58. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/utils/wsdl.py +0 -0
  59. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/utils/zeep.py +0 -0
  60. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
  61. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
  62. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
  63. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
  64. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
  65. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
  66. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
  67. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
  68. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
  69. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
  70. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
  71. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
  72. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
  73. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
  74. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
  75. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/display/display.wsdl +0 -0
  76. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
  77. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/display.wsdl +0 -0
  78. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
  79. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
  80. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
  81. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
  82. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
  83. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
  84. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
  85. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
  86. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
  87. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
  88. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
  89. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/receiver.wsdl +0 -0
  90. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/recording.wsdl +0 -0
  91. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/replay.wsdl +0 -0
  92. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
  93. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
  94. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/schema/common.xsd +0 -0
  95. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
  96. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
  97. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/search.wsdl +0 -0
  98. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
  99. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
  100. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
  101. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
  102. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
  103. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
  104. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
  105. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/analytics/index.html +0 -0
  106. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
  107. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
  108. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
  109. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
  110. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
  111. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
  112. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
  113. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
  114. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
  115. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
  116. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
  117. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
  118. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif_python.egg-info/dependency_links.txt +0 -0
  119. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif_python.egg-info/entry_points.txt +0 -0
  120. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif_python.egg-info/requires.txt +0 -0
  121. {onvif_python-0.1.5 → onvif_python-0.1.7}/onvif_python.egg-info/top_level.txt +0 -0
  122. {onvif_python-0.1.5 → onvif_python-0.1.7}/setup.cfg +0 -0
  123. {onvif_python-0.1.5 → onvif_python-0.1.7}/setup.py +0 -0
  124. {onvif_python-0.1.5 → onvif_python-0.1.7}/tests/test_client.py +0 -0
  125. {onvif_python-0.1.5 → onvif_python-0.1.7}/tests/test_core.py +0 -0
  126. {onvif_python-0.1.5 → onvif_python-0.1.7}/tests/test_error_handlers.py +0 -0
  127. {onvif_python-0.1.5 → onvif_python-0.1.7}/tests/test_exceptions.py +0 -0
  128. {onvif_python-0.1.5 → onvif_python-0.1.7}/tests/test_wsdl_map.py +0 -0
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onvif-python
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: A comprehensive and developer-friendly Python library for working with ONVIF-compliant devices
5
5
  Author-email: Nirsimetri Technologies® <open@nirsimetri.com>
6
6
  Maintainer: Kaburagi
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
@@ -40,7 +41,7 @@ Dynamic: license-file
40
41
 
41
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)
42
43
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/nirsimetri/onvif-python)
43
- [![PyPI](https://img.shields.io/badge/PyPI-0.1.5-orange?logo=archive)](https://pypi.org/project/onvif-python/)
44
+ [![PyPI](https://img.shields.io/badge/PyPI-0.1.7-orange?logo=archive)](https://pypi.org/project/onvif-python/)
44
45
  [![Downloads](https://img.shields.io/pypi/dm/onvif-python?label=Downloads&color=red)](https://clickpy.clickhouse.com/dashboard/onvif-python)
45
46
  <br>
46
47
  [![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)
@@ -92,9 +93,33 @@ pip install .
92
93
  > [!TIP]
93
94
  > You can view the complete documentation automatically generated by DeepWiki via the [onvif-python AI Wiki](https://deepwiki.com/nirsimetri/onvif-python) link. We currently do not have an official documentation site. Help us create more examples and helpful documentation by [contributing](https://github.com/nirsimetri/onvif-python?tab=contributing-ov-file).
94
95
 
95
- Below are simple examples to help you get started with the ONVIF Python library. These demonstrate how to connect to an ONVIF-compliant device and retrieve basic device information.
96
+ Below are simple examples to help you get started with the ONVIF Python library. These demonstrate how to discover and connect to ONVIF-compliant devices and retrieve basic device information.
96
97
 
97
- **1. Initialize the ONVIFClient**
98
+ **1. Discover ONVIF Devices (Optional)**
99
+
100
+ Use `ONVIFDiscovery` (applied at [`>=v0.1.6`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.1.6)) to automatically find ONVIF devices on your local network:
101
+
102
+ ```python
103
+ from onvif import ONVIFDiscovery
104
+
105
+ # Create discovery instance
106
+ discovery = ONVIFDiscovery(timeout=5)
107
+
108
+ # Discover devices
109
+ devices = discovery.discover()
110
+
111
+ # Or with
112
+ # Discover with search filter by types or scopes (case-insensitive substring match)
113
+ devices = discovery.discover(search="Profile/Streaming")
114
+
115
+ # Display discovered devices
116
+ for device in devices:
117
+ print(f"Found device at {device['host']}:{device['port']}")
118
+ print(f" Scopes: {device.get('scopes', [])}")
119
+ print(f" XAddrs: {device['xaddrs']}")
120
+ ```
121
+
122
+ **2. Initialize the ONVIFClient**
98
123
 
99
124
  Create an instance of `ONVIFClient` by providing your device's IP address, port, username, and password:
100
125
 
@@ -111,7 +136,7 @@ client = ONVIFClient(
111
136
  )
112
137
  ```
113
138
 
114
- **2. Create Service Instance**
139
+ **3. Create Service Instance**
115
140
 
116
141
  `ONVIFClient` provides several main services that can be accessed via the following methods:
117
142
 
@@ -130,7 +155,7 @@ device = client.devicemgmt() # Device Management (Core)
130
155
  media = client.media() # Media
131
156
  ```
132
157
 
133
- **3. Get Device Information**
158
+ **4. Get Device Information**
134
159
 
135
160
  Retrieve basic information about the device, such as manufacturer, model, firmware version, and serial number using `devicemgmt()` service:
136
161
 
@@ -140,7 +165,7 @@ print(info)
140
165
  # Example output: {'Manufacturer': '..', 'Model': '..', 'FirmwareVersion': '..', 'SerialNumber': '..'}
141
166
  ```
142
167
 
143
- **4. Get RTSP URL**
168
+ **5. Get RTSP URL**
144
169
 
145
170
  Retrieve the RTSP stream URL for live video streaming from the device using `media()` service:
146
171
 
@@ -173,6 +198,7 @@ This library includes a powerful command-line interface (CLI) for interacting wi
173
198
 
174
199
  ### Features
175
200
 
201
+ - **Device Discovery:** Automatic ONVIF device discovery on local network using WS-Discovery protocol.
176
202
  - **Interactive Shell:** A user-friendly shell with tab completion, command history, and colorized output.
177
203
  - **Direct Command Execution:** Run ONVIF commands directly from the terminal for scripting and automation.
178
204
  - **Automatic Discovery:** Automatically detects available services on the device.
@@ -211,11 +237,12 @@ This library includes a powerful command-line interface (CLI) for interacting wi
211
237
  <summary><b>1. Direct CLI</b></summary>
212
238
 
213
239
  ```bash
214
- usage: onvif [-h] [--host HOST] [--port PORT] [--username USERNAME] [--password PASSWORD] [--discover] [--timeout TIMEOUT] [--https] [--no-verify]
215
- [--no-patch] [--interactive] [--debug] [--wsdl WSDL] [--cache {all,db,mem,none}] [--health-check-interval HEALTH_CHECK_INTERVAL] [--version]
240
+ usage: onvif [-h] [--host HOST] [--port PORT] [--username USERNAME] [--password PASSWORD] [--discover] [--search SEARCH] [--timeout TIMEOUT] [--https]
241
+ [--no-verify] [--no-patch] [--interactive] [--debug] [--wsdl WSDL] [--cache {all,db,mem,none}]
242
+ [--health-check-interval HEALTH_CHECK_INTERVAL] [--version]
216
243
  [service] [method] [params ...]
217
244
 
218
- ONVIF Terminal Client — v0.1.5
245
+ ONVIF Terminal Client — v0.1.7
219
246
  https://github.com/nirsimetri/onvif-python
220
247
 
221
248
  positional arguments:
@@ -232,6 +259,8 @@ options:
232
259
  --password PASSWORD, -p PASSWORD
233
260
  Password for authentication
234
261
  --discover, -d Discover ONVIF devices on the network using WS-Discovery
262
+ --search SEARCH, -s SEARCH
263
+ Filter discovered devices by types or scopes (case-insensitive substring match)
235
264
  --timeout TIMEOUT Connection timeout in seconds (default: 10)
236
265
  --https Use HTTPS instead of HTTP
237
266
  --no-verify Disable SSL certificate verification
@@ -251,6 +280,11 @@ Examples:
251
280
  onvif media GetProfiles --discover --username admin
252
281
  onvif -d -i
253
282
 
283
+ # Discover with filtering
284
+ onvif --discover --search ptz --interactive
285
+ onvif -d -s "C210" -i
286
+ onvif -d -s "audio_encoder" -u admin -p admin123 -i
287
+
254
288
  # Direct command execution
255
289
  onvif devicemgmt GetCapabilities Category=All --host 192.168.1.17 --port 8000 --username admin --password admin123
256
290
  onvif ptz ContinuousMove ProfileToken=Profile_1 Velocity={"PanTilt": {"x": -0.1, "y": 0}} --host 192.168.1.17 --port 8000 --username admin --password admin123
@@ -272,7 +306,7 @@ Examples:
272
306
  <summary><b>2. Interactive Shell</b></summary>
273
307
 
274
308
  ```bash
275
- ONVIF Interactive Shell Commands - v0.1.5
309
+ ONVIF Interactive Shell — v0.1.7
276
310
  https://github.com/nirsimetri/onvif-python
277
311
 
278
312
  Basic Commands:
@@ -405,6 +439,10 @@ onvif --discover --username admin --password admin123 --interactive
405
439
  # Short form
406
440
  onvif -d -u admin -p admin123 -i
407
441
 
442
+ # Discover with search filter
443
+ onvif --discover --search "C210" --interactive
444
+ onvif -d -s ptz -u admin -p admin123 -i
445
+
408
446
  # Discover and interactive (will prompt for credentials)
409
447
  onvif -d -i
410
448
  ```
@@ -2,7 +2,7 @@
2
2
 
3
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)
4
4
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/nirsimetri/onvif-python)
5
- [![PyPI](https://img.shields.io/badge/PyPI-0.1.5-orange?logo=archive)](https://pypi.org/project/onvif-python/)
5
+ [![PyPI](https://img.shields.io/badge/PyPI-0.1.7-orange?logo=archive)](https://pypi.org/project/onvif-python/)
6
6
  [![Downloads](https://img.shields.io/pypi/dm/onvif-python?label=Downloads&color=red)](https://clickpy.clickhouse.com/dashboard/onvif-python)
7
7
  <br>
8
8
  [![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)
@@ -54,9 +54,33 @@ pip install .
54
54
  > [!TIP]
55
55
  > You can view the complete documentation automatically generated by DeepWiki via the [onvif-python AI Wiki](https://deepwiki.com/nirsimetri/onvif-python) link. We currently do not have an official documentation site. Help us create more examples and helpful documentation by [contributing](https://github.com/nirsimetri/onvif-python?tab=contributing-ov-file).
56
56
 
57
- Below are simple examples to help you get started with the ONVIF Python library. These demonstrate how to connect to an ONVIF-compliant device and retrieve basic device information.
57
+ Below are simple examples to help you get started with the ONVIF Python library. These demonstrate how to discover and connect to ONVIF-compliant devices and retrieve basic device information.
58
58
 
59
- **1. Initialize the ONVIFClient**
59
+ **1. Discover ONVIF Devices (Optional)**
60
+
61
+ Use `ONVIFDiscovery` (applied at [`>=v0.1.6`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.1.6)) to automatically find ONVIF devices on your local network:
62
+
63
+ ```python
64
+ from onvif import ONVIFDiscovery
65
+
66
+ # Create discovery instance
67
+ discovery = ONVIFDiscovery(timeout=5)
68
+
69
+ # Discover devices
70
+ devices = discovery.discover()
71
+
72
+ # Or with
73
+ # Discover with search filter by types or scopes (case-insensitive substring match)
74
+ devices = discovery.discover(search="Profile/Streaming")
75
+
76
+ # Display discovered devices
77
+ for device in devices:
78
+ print(f"Found device at {device['host']}:{device['port']}")
79
+ print(f" Scopes: {device.get('scopes', [])}")
80
+ print(f" XAddrs: {device['xaddrs']}")
81
+ ```
82
+
83
+ **2. Initialize the ONVIFClient**
60
84
 
61
85
  Create an instance of `ONVIFClient` by providing your device's IP address, port, username, and password:
62
86
 
@@ -73,7 +97,7 @@ client = ONVIFClient(
73
97
  )
74
98
  ```
75
99
 
76
- **2. Create Service Instance**
100
+ **3. Create Service Instance**
77
101
 
78
102
  `ONVIFClient` provides several main services that can be accessed via the following methods:
79
103
 
@@ -92,7 +116,7 @@ device = client.devicemgmt() # Device Management (Core)
92
116
  media = client.media() # Media
93
117
  ```
94
118
 
95
- **3. Get Device Information**
119
+ **4. Get Device Information**
96
120
 
97
121
  Retrieve basic information about the device, such as manufacturer, model, firmware version, and serial number using `devicemgmt()` service:
98
122
 
@@ -102,7 +126,7 @@ print(info)
102
126
  # Example output: {'Manufacturer': '..', 'Model': '..', 'FirmwareVersion': '..', 'SerialNumber': '..'}
103
127
  ```
104
128
 
105
- **4. Get RTSP URL**
129
+ **5. Get RTSP URL**
106
130
 
107
131
  Retrieve the RTSP stream URL for live video streaming from the device using `media()` service:
108
132
 
@@ -135,6 +159,7 @@ This library includes a powerful command-line interface (CLI) for interacting wi
135
159
 
136
160
  ### Features
137
161
 
162
+ - **Device Discovery:** Automatic ONVIF device discovery on local network using WS-Discovery protocol.
138
163
  - **Interactive Shell:** A user-friendly shell with tab completion, command history, and colorized output.
139
164
  - **Direct Command Execution:** Run ONVIF commands directly from the terminal for scripting and automation.
140
165
  - **Automatic Discovery:** Automatically detects available services on the device.
@@ -173,11 +198,12 @@ This library includes a powerful command-line interface (CLI) for interacting wi
173
198
  <summary><b>1. Direct CLI</b></summary>
174
199
 
175
200
  ```bash
176
- usage: onvif [-h] [--host HOST] [--port PORT] [--username USERNAME] [--password PASSWORD] [--discover] [--timeout TIMEOUT] [--https] [--no-verify]
177
- [--no-patch] [--interactive] [--debug] [--wsdl WSDL] [--cache {all,db,mem,none}] [--health-check-interval HEALTH_CHECK_INTERVAL] [--version]
201
+ usage: onvif [-h] [--host HOST] [--port PORT] [--username USERNAME] [--password PASSWORD] [--discover] [--search SEARCH] [--timeout TIMEOUT] [--https]
202
+ [--no-verify] [--no-patch] [--interactive] [--debug] [--wsdl WSDL] [--cache {all,db,mem,none}]
203
+ [--health-check-interval HEALTH_CHECK_INTERVAL] [--version]
178
204
  [service] [method] [params ...]
179
205
 
180
- ONVIF Terminal Client — v0.1.5
206
+ ONVIF Terminal Client — v0.1.7
181
207
  https://github.com/nirsimetri/onvif-python
182
208
 
183
209
  positional arguments:
@@ -194,6 +220,8 @@ options:
194
220
  --password PASSWORD, -p PASSWORD
195
221
  Password for authentication
196
222
  --discover, -d Discover ONVIF devices on the network using WS-Discovery
223
+ --search SEARCH, -s SEARCH
224
+ Filter discovered devices by types or scopes (case-insensitive substring match)
197
225
  --timeout TIMEOUT Connection timeout in seconds (default: 10)
198
226
  --https Use HTTPS instead of HTTP
199
227
  --no-verify Disable SSL certificate verification
@@ -213,6 +241,11 @@ Examples:
213
241
  onvif media GetProfiles --discover --username admin
214
242
  onvif -d -i
215
243
 
244
+ # Discover with filtering
245
+ onvif --discover --search ptz --interactive
246
+ onvif -d -s "C210" -i
247
+ onvif -d -s "audio_encoder" -u admin -p admin123 -i
248
+
216
249
  # Direct command execution
217
250
  onvif devicemgmt GetCapabilities Category=All --host 192.168.1.17 --port 8000 --username admin --password admin123
218
251
  onvif ptz ContinuousMove ProfileToken=Profile_1 Velocity={"PanTilt": {"x": -0.1, "y": 0}} --host 192.168.1.17 --port 8000 --username admin --password admin123
@@ -234,7 +267,7 @@ Examples:
234
267
  <summary><b>2. Interactive Shell</b></summary>
235
268
 
236
269
  ```bash
237
- ONVIF Interactive Shell Commands - v0.1.5
270
+ ONVIF Interactive Shell — v0.1.7
238
271
  https://github.com/nirsimetri/onvif-python
239
272
 
240
273
  Basic Commands:
@@ -367,6 +400,10 @@ onvif --discover --username admin --password admin123 --interactive
367
400
  # Short form
368
401
  onvif -d -u admin -p admin123 -i
369
402
 
403
+ # Discover with search filter
404
+ onvif --discover --search "C210" --interactive
405
+ onvif -d -s ptz -u admin -p admin123 -i
406
+
370
407
  # Discover and interactive (will prompt for credentials)
371
408
  onvif -d -i
372
409
  ```
@@ -2,7 +2,13 @@
2
2
 
3
3
  from .client import ONVIFClient
4
4
  from .operator import ONVIFOperator, CacheMode
5
- from .utils import ONVIFWSDL, ONVIFOperationException, ONVIFErrorHandler, ZeepPatcher
5
+ from .utils import (
6
+ ONVIFWSDL,
7
+ ONVIFOperationException,
8
+ ONVIFErrorHandler,
9
+ ZeepPatcher,
10
+ ONVIFDiscovery,
11
+ )
6
12
  from .cli import main as ONVIFCLI
7
13
 
8
14
  __all__ = [
@@ -14,4 +20,5 @@ __all__ = [
14
20
  "ONVIFErrorHandler",
15
21
  "ZeepPatcher",
16
22
  "ONVIFCLI",
23
+ "ONVIFDiscovery",
17
24
  ]
@@ -191,7 +191,7 @@ class InteractiveShell(cmd.Cmd):
191
191
  " / __ \\/ | / / | / / _/ ____/",
192
192
  " / / / / |/ /| | / // // /_ ",
193
193
  "/ /_/ / /| / | |/ // // __/ ",
194
- "\\____/_/ |_/ |___/___/_/ v0.1.5",
194
+ "\\____/_/ |_/ |___/___/_/ v0.1.7",
195
195
  " ",
196
196
  ]
197
197
 
@@ -240,25 +240,27 @@ class InteractiveShell(cmd.Cmd):
240
240
  while not self._stop_health_check.is_set():
241
241
  sock = None
242
242
  try:
243
- sock = socket.create_connection(
244
- (self.args.host, self.args.port), timeout=5.0
245
- )
246
-
247
- # If HTTPS, perform a short TLS handshake check
243
+ # For HTTPS, use ssl.create_connection for proper TLS handling
248
244
  if getattr(self.args, "https", False):
249
245
  context = ssl.create_default_context()
250
246
  context.minimum_version = ssl.TLSVersion.TLSv1_2
251
247
  context.check_hostname = False
252
248
  context.verify_mode = ssl.CERT_NONE
253
249
 
254
- with context.wrap_socket(
255
- sock, server_hostname=self.args.host
256
- ) as ssock:
257
- ssock.settimeout(5)
258
- # Try a minimal handshake
259
- ssock.do_handshake()
250
+ # Use ssl.create_connection instead of wrapping existing socket
251
+ sock = context.wrap_socket(
252
+ socket.socket(socket.AF_INET, socket.SOCK_STREAM),
253
+ server_hostname=self.args.host,
254
+ )
255
+ sock.settimeout(5.0)
256
+ sock.connect((self.args.host, self.args.port))
257
+ else:
258
+ # For HTTP, simple TCP connection check
259
+ sock = socket.create_connection(
260
+ (self.args.host, self.args.port), timeout=5.0
261
+ )
260
262
 
261
- # If we reach here, the connection (and TLS if applicable) succeeded
263
+ # Connection successful
262
264
  pass
263
265
 
264
266
  except (
@@ -368,7 +370,7 @@ class InteractiveShell(cmd.Cmd):
368
370
 
369
371
  return parts
370
372
 
371
- def _display_grid(self, items, force_recalc=False):
373
+ def _display_grid(self, items):
372
374
  """Display items in grid format matching TAB completion (vertical layout)"""
373
375
  if not items:
374
376
  return
@@ -393,7 +395,7 @@ class InteractiveShell(cmd.Cmd):
393
395
 
394
396
  # Column width calculation: use same algorithm as cmd.Cmd.columnize()
395
397
  # Python's columnize uses: maxlen + 2 if maxlen > 0 else 2
396
- col_width = longest_length + 1 if longest_length > 0 else 2
398
+ col_width = longest_length + 2 if longest_length > 0 else 2
397
399
 
398
400
  # Calculate number of columns that fit
399
401
  num_cols = max(1, term_width // col_width)
@@ -661,28 +663,17 @@ class InteractiveShell(cmd.Cmd):
661
663
  print("^C")
662
664
  line = ""
663
665
 
664
- # Handle TAB completion on empty line
665
- if line == "\t":
666
- print() # Newline before grid
667
- if self.current_service:
668
- self._display_grid(get_service_methods(self.current_service))
669
- else:
670
- services = get_device_available_services(self.client)
671
- commands = self.base_commands
672
- self._display_grid(services + commands)
673
- line = "" # Reset line to show prompt again
674
-
675
666
  line = self.precmd(line)
676
667
  stop = self.onecmd(line)
677
668
  stop = self.postcmd(stop, line)
678
669
 
679
- def columnize(self, list, displaywidth=80):
670
+ def columnize(self, items, displaywidth=80):
680
671
  """Override columnize to use grid format for TAB completion"""
681
- if not list:
672
+ if not items:
682
673
  return
683
674
 
684
675
  # Use our grid display for TAB completion with coloring enabled
685
- self._display_grid(list, force_recalc=True)
676
+ self._display_grid(items)
686
677
 
687
678
  def print_topics(self, header, cmds, cmdlen, maxcol):
688
679
  """Override print_topics to use grid format for TAB completion"""
@@ -694,7 +685,7 @@ class InteractiveShell(cmd.Cmd):
694
685
  self.stdout.write("%s\n" % str(header))
695
686
 
696
687
  # Use our grid display for TAB completion with coloring enabled
697
- self._display_grid(cmds, force_recalc=True)
688
+ self._display_grid(cmds)
698
689
 
699
690
  def do_capabilities(self, line):
700
691
  """Show device capabilities in service format"""
@@ -933,7 +924,7 @@ class InteractiveShell(cmd.Cmd):
933
924
  return
934
925
 
935
926
  if not operation_name:
936
- print(f"Usage: {colorize('type <operation_name>', 'yellow')}")
927
+ print("Usage: type <operation_name>")
937
928
  return
938
929
 
939
930
  if not hasattr(self.current_service, operation_name):
@@ -1394,7 +1385,7 @@ class InteractiveShell(cmd.Cmd):
1394
1385
  super().do_help(line)
1395
1386
  else:
1396
1387
  help_text = f"""
1397
- {colorize('ONVIF Interactive Shell Commands - v0.1.5', 'cyan')}\n{colorize('https://github.com/nirsimetri/onvif-python', 'white')}
1388
+ {colorize('ONVIF Interactive Shell — v0.1.7', 'cyan')}\n{colorize('https://github.com/nirsimetri/onvif-python', 'white')}
1398
1389
 
1399
1390
  {colorize('Basic Commands:', 'yellow')}
1400
1391
  capabilities, caps - Show device capabilities