onvif-python 0.1.4__tar.gz → 0.1.6__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.4/onvif_python.egg-info → onvif_python-0.1.6}/PKG-INFO +58 -12
  2. {onvif_python-0.1.4 → onvif_python-0.1.6}/README.md +57 -11
  3. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/__init__.py +8 -1
  4. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/cli/interactive.py +126 -40
  5. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/cli/main.py +20 -163
  6. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/cli/utils.py +205 -51
  7. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/client.py +4 -4
  8. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/utils/__init__.py +2 -0
  9. onvif_python-0.1.6/onvif/utils/discovery.py +297 -0
  10. {onvif_python-0.1.4 → onvif_python-0.1.6/onvif_python.egg-info}/PKG-INFO +58 -12
  11. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif_python.egg-info/SOURCES.txt +1 -0
  12. {onvif_python-0.1.4 → onvif_python-0.1.6}/pyproject.toml +1 -1
  13. {onvif_python-0.1.4 → onvif_python-0.1.6}/LICENSE.md +0 -0
  14. {onvif_python-0.1.4 → onvif_python-0.1.6}/MANIFEST.in +0 -0
  15. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/cli/__init__.py +0 -0
  16. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/cli/__main__.py +0 -0
  17. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/operator.py +0 -0
  18. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/__init__.py +0 -0
  19. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/accesscontrol.py +0 -0
  20. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/accessrules.py +0 -0
  21. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/actionengine.py +0 -0
  22. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/analytics/analytics.py +0 -0
  23. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/analytics/ruleengine.py +0 -0
  24. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/analyticsdevice.py +0 -0
  25. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/appmgmt.py +0 -0
  26. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/authenticationbehavior.py +0 -0
  27. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/credential.py +0 -0
  28. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/deviceio.py +0 -0
  29. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/devicemgmt.py +0 -0
  30. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/display.py +0 -0
  31. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/doorcontrol.py +0 -0
  32. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/events/events.py +0 -0
  33. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/events/notification.py +0 -0
  34. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/events/pullpoint.py +0 -0
  35. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/events/subscription.py +0 -0
  36. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/imaging.py +0 -0
  37. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/media.py +0 -0
  38. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/media2.py +0 -0
  39. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/provisioning.py +0 -0
  40. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/ptz.py +0 -0
  41. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/receiver.py +0 -0
  42. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/recording.py +0 -0
  43. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/replay.py +0 -0
  44. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/schedule.py +0 -0
  45. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/search.py +0 -0
  46. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/security/advancedsecurity.py +0 -0
  47. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/security/authorizationserver.py +0 -0
  48. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/security/dot1x.py +0 -0
  49. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/security/jwt.py +0 -0
  50. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/security/keystore.py +0 -0
  51. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/security/mediasigning.py +0 -0
  52. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/security/tlsserver.py +0 -0
  53. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/thermal.py +0 -0
  54. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/uplink.py +0 -0
  55. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/utils/error_handlers.py +0 -0
  56. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/utils/exceptions.py +0 -0
  57. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/utils/wsdl.py +0 -0
  58. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/utils/xml_capture.py +0 -0
  59. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/utils/zeep.py +0 -0
  60. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
  61. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
  62. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
  63. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
  64. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
  65. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
  66. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
  67. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
  68. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
  69. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
  70. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
  71. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
  72. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
  73. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
  74. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
  75. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/display/display.wsdl +0 -0
  76. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
  77. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/display.wsdl +0 -0
  78. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
  79. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
  80. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
  81. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
  82. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
  83. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
  84. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
  85. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
  86. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
  87. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
  88. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
  89. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/receiver.wsdl +0 -0
  90. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/recording.wsdl +0 -0
  91. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/replay.wsdl +0 -0
  92. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
  93. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
  94. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/schema/common.xsd +0 -0
  95. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
  96. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
  97. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/search.wsdl +0 -0
  98. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
  99. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
  100. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
  101. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
  102. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
  103. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
  104. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
  105. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/analytics/index.html +0 -0
  106. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
  107. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
  108. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
  109. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
  110. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
  111. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
  112. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
  113. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
  114. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
  115. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
  116. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
  117. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
  118. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif_python.egg-info/dependency_links.txt +0 -0
  119. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif_python.egg-info/entry_points.txt +0 -0
  120. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif_python.egg-info/requires.txt +0 -0
  121. {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif_python.egg-info/top_level.txt +0 -0
  122. {onvif_python-0.1.4 → onvif_python-0.1.6}/setup.cfg +0 -0
  123. {onvif_python-0.1.4 → onvif_python-0.1.6}/setup.py +0 -0
  124. {onvif_python-0.1.4 → onvif_python-0.1.6}/tests/test_client.py +0 -0
  125. {onvif_python-0.1.4 → onvif_python-0.1.6}/tests/test_core.py +0 -0
  126. {onvif_python-0.1.4 → onvif_python-0.1.6}/tests/test_error_handlers.py +0 -0
  127. {onvif_python-0.1.4 → onvif_python-0.1.6}/tests/test_exceptions.py +0 -0
  128. {onvif_python-0.1.4 → onvif_python-0.1.6}/tests/test_wsdl_map.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onvif-python
3
- Version: 0.1.4
3
+ Version: 0.1.6
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
@@ -38,9 +38,9 @@ Dynamic: license-file
38
38
 
39
39
  # ONVIF Python
40
40
 
41
- [![License](https://img.shields.io/badge/License-MIT-blue)](https://github.com/nirsimetri/onvif-python?tab=MIT-1-ov-file)
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)
42
42
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/nirsimetri/onvif-python)
43
- [![PyPI](https://img.shields.io/badge/PyPI-0.1.4-orange?logo=archive)](https://pypi.org/project/onvif-python/)
43
+ [![PyPI](https://img.shields.io/badge/PyPI-0.1.6-orange?logo=archive)](https://pypi.org/project/onvif-python/)
44
44
  [![Downloads](https://img.shields.io/pypi/dm/onvif-python?label=Downloads&color=red)](https://clickpy.clickhouse.com/dashboard/onvif-python)
45
45
  <br>
46
46
  [![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 +92,29 @@ pip install .
92
92
  > [!TIP]
93
93
  > 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
94
 
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.
95
+ 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
96
 
97
- **1. Initialize the ONVIFClient**
97
+ **1. Discover ONVIF Devices (Optional)**
98
+
99
+ 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:
100
+
101
+ ```python
102
+ from onvif import ONVIFDiscovery
103
+
104
+ # Create discovery instance
105
+ discovery = ONVIFDiscovery(timeout=5)
106
+
107
+ # Discover devices
108
+ devices = discovery.discover()
109
+
110
+ # Display discovered devices
111
+ for device in devices:
112
+ print(f"Found device at {device['host']}:{device['port']}")
113
+ print(f" Scopes: {device.get('scopes', [])}")
114
+ print(f" XAddrs: {device['xaddrs']}")
115
+ ```
116
+
117
+ **2. Initialize the ONVIFClient**
98
118
 
99
119
  Create an instance of `ONVIFClient` by providing your device's IP address, port, username, and password:
100
120
 
@@ -111,7 +131,7 @@ client = ONVIFClient(
111
131
  )
112
132
  ```
113
133
 
114
- **2. Create Service Instance**
134
+ **3. Create Service Instance**
115
135
 
116
136
  `ONVIFClient` provides several main services that can be accessed via the following methods:
117
137
 
@@ -130,7 +150,7 @@ device = client.devicemgmt() # Device Management (Core)
130
150
  media = client.media() # Media
131
151
  ```
132
152
 
133
- **3. Get Device Information**
153
+ **4. Get Device Information**
134
154
 
135
155
  Retrieve basic information about the device, such as manufacturer, model, firmware version, and serial number using `devicemgmt()` service:
136
156
 
@@ -140,7 +160,7 @@ print(info)
140
160
  # Example output: {'Manufacturer': '..', 'Model': '..', 'FirmwareVersion': '..', 'SerialNumber': '..'}
141
161
  ```
142
162
 
143
- **4. Get RTSP URL**
163
+ **5. Get RTSP URL**
144
164
 
145
165
  Retrieve the RTSP stream URL for live video streaming from the device using `media()` service:
146
166
 
@@ -173,6 +193,7 @@ This library includes a powerful command-line interface (CLI) for interacting wi
173
193
 
174
194
  ### Features
175
195
 
196
+ - **Device Discovery:** Automatic ONVIF device discovery on local network using WS-Discovery protocol.
176
197
  - **Interactive Shell:** A user-friendly shell with tab completion, command history, and colorized output.
177
198
  - **Direct Command Execution:** Run ONVIF commands directly from the terminal for scripting and automation.
178
199
  - **Automatic Discovery:** Automatically detects available services on the device.
@@ -212,10 +233,10 @@ This library includes a powerful command-line interface (CLI) for interacting wi
212
233
 
213
234
  ```bash
214
235
  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}] [--version]
236
+ [--no-patch] [--interactive] [--debug] [--wsdl WSDL] [--cache {all,db,mem,none}] [--health-check-interval HEALTH_CHECK_INTERVAL] [--version]
216
237
  [service] [method] [params ...]
217
238
 
218
- ONVIF Terminal Client — v0.1.4
239
+ ONVIF Terminal Client — v0.1.6
219
240
  https://github.com/nirsimetri/onvif-python
220
241
 
221
242
  positional arguments:
@@ -241,6 +262,8 @@ options:
241
262
  --wsdl WSDL Custom WSDL directory path
242
263
  --cache {all,db,mem,none}
243
264
  Caching mode for ONVIFClient (default: all). 'all': memory+disk, 'db': disk-only, 'mem': memory-only, 'none': disabled.
265
+ --health-check-interval HEALTH_CHECK_INTERVAL, -hci HEALTH_CHECK_INTERVAL
266
+ Health check interval in seconds for interactive mode (default: 10)
244
267
  --version, -v Show ONVIF CLI version and exit
245
268
 
246
269
  Examples:
@@ -270,7 +293,7 @@ Examples:
270
293
  <summary><b>2. Interactive Shell</b></summary>
271
294
 
272
295
  ```bash
273
- ONVIF Interactive Shell Commands - v0.1.4
296
+ ONVIF Interactive Shell Commands - v0.1.6
274
297
  https://github.com/nirsimetri/onvif-python
275
298
 
276
299
  Basic Commands:
@@ -282,6 +305,7 @@ Basic Commands:
282
305
 
283
306
  Navigation Commands:
284
307
  <service> - Enter service mode (e.g., devicemgmt, media)
308
+ <service> <argument> - Enter service mode with argument (e.g. pullpoint SubscriptionRef=<value>)
285
309
  cd <service> - Enter service mode (alias)
286
310
  ls - List commands/services/methods in grid format
287
311
  up - Exit current service mode (go up one level)
@@ -301,6 +325,7 @@ Method Execution:
301
325
  Data Management:
302
326
  store <name> - Store last result with a name
303
327
  show <name> - Show stored data
328
+ show <name>[0] - Show element at index (for lists)
304
329
  show <name>.attribute - Show specific attribute
305
330
  show - List all stored data
306
331
  rm <name> - Remove stored data by name
@@ -368,6 +393,27 @@ If you omit the username or password, you will be prompted to enter them securel
368
393
  > [!IMPORTANT]
369
394
  > You can see all the other commands available in the interactive shell by trying it out directly. The interactive shell runs periodic background health checks to detect connection loss. It uses silent TCP pings to avoid interrupting your work and will automatically exit if the device is unreachable, similar to an SSH session.
370
395
 
396
+ **Command Chaining with `&&`:**
397
+
398
+ The CLI supports chaining multiple commands in a single line using the `&&` operator, allowing you to execute sequential operations efficiently:
399
+
400
+ ```bash
401
+ # Enter service and execute method in one line
402
+ 192.168.1.17:8000 > media && GetProfiles && store profiles
403
+
404
+ # Chain multiple method calls
405
+ 192.168.1.17:8000 > devicemgmt && GetDeviceInformation && store device_info
406
+
407
+ # Complex workflow
408
+ 192.168.1.17:8000 > media && GetProfiles && store profiles && up && imaging && GetImagingSettings VideoSourceToken=$profiles[0].VideoSourceConfiguration.SourceToken
409
+ ```
410
+
411
+ This feature is particularly useful for:
412
+ - Quick operations without entering service mode
413
+ - Scripting repetitive tasks
414
+ - Testing workflows
415
+ - Automating multi-step procedures
416
+
371
417
  **2. Device Discovery (WS-Discovery)**
372
418
 
373
419
  The CLI includes automatic ONVIF device discovery using the WS-Discovery protocol. This feature allows you to find all ONVIF-compliant devices on your local network without knowing their IP addresses beforehand.
@@ -491,7 +537,7 @@ The `ONVIFClient` class provides various configuration options to customize the
491
537
 
492
538
  | Parameter | Type | Required | Default | Description |
493
539
  |-----------|------|----------|---------|-------------|
494
- | `cache` | `CacheMode` | ❌ No | `CacheMode.ALL` | WSDL caching strategy (see [Cache Modes](#cache-modes) below) |
540
+ | `cache` | `CacheMode` | ❌ No | `CacheMode.ALL` | WSDL caching strategy (see **Cache Modes** below) |
495
541
 
496
542
  </details>
497
543
 
@@ -1,8 +1,8 @@
1
1
  # ONVIF Python
2
2
 
3
- [![License](https://img.shields.io/badge/License-MIT-blue)](https://github.com/nirsimetri/onvif-python?tab=MIT-1-ov-file)
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.4-orange?logo=archive)](https://pypi.org/project/onvif-python/)
5
+ [![PyPI](https://img.shields.io/badge/PyPI-0.1.6-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,29 @@ 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
+ # Display discovered devices
73
+ for device in devices:
74
+ print(f"Found device at {device['host']}:{device['port']}")
75
+ print(f" Scopes: {device.get('scopes', [])}")
76
+ print(f" XAddrs: {device['xaddrs']}")
77
+ ```
78
+
79
+ **2. Initialize the ONVIFClient**
60
80
 
61
81
  Create an instance of `ONVIFClient` by providing your device's IP address, port, username, and password:
62
82
 
@@ -73,7 +93,7 @@ client = ONVIFClient(
73
93
  )
74
94
  ```
75
95
 
76
- **2. Create Service Instance**
96
+ **3. Create Service Instance**
77
97
 
78
98
  `ONVIFClient` provides several main services that can be accessed via the following methods:
79
99
 
@@ -92,7 +112,7 @@ device = client.devicemgmt() # Device Management (Core)
92
112
  media = client.media() # Media
93
113
  ```
94
114
 
95
- **3. Get Device Information**
115
+ **4. Get Device Information**
96
116
 
97
117
  Retrieve basic information about the device, such as manufacturer, model, firmware version, and serial number using `devicemgmt()` service:
98
118
 
@@ -102,7 +122,7 @@ print(info)
102
122
  # Example output: {'Manufacturer': '..', 'Model': '..', 'FirmwareVersion': '..', 'SerialNumber': '..'}
103
123
  ```
104
124
 
105
- **4. Get RTSP URL**
125
+ **5. Get RTSP URL**
106
126
 
107
127
  Retrieve the RTSP stream URL for live video streaming from the device using `media()` service:
108
128
 
@@ -135,6 +155,7 @@ This library includes a powerful command-line interface (CLI) for interacting wi
135
155
 
136
156
  ### Features
137
157
 
158
+ - **Device Discovery:** Automatic ONVIF device discovery on local network using WS-Discovery protocol.
138
159
  - **Interactive Shell:** A user-friendly shell with tab completion, command history, and colorized output.
139
160
  - **Direct Command Execution:** Run ONVIF commands directly from the terminal for scripting and automation.
140
161
  - **Automatic Discovery:** Automatically detects available services on the device.
@@ -174,10 +195,10 @@ This library includes a powerful command-line interface (CLI) for interacting wi
174
195
 
175
196
  ```bash
176
197
  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}] [--version]
198
+ [--no-patch] [--interactive] [--debug] [--wsdl WSDL] [--cache {all,db,mem,none}] [--health-check-interval HEALTH_CHECK_INTERVAL] [--version]
178
199
  [service] [method] [params ...]
179
200
 
180
- ONVIF Terminal Client — v0.1.4
201
+ ONVIF Terminal Client — v0.1.6
181
202
  https://github.com/nirsimetri/onvif-python
182
203
 
183
204
  positional arguments:
@@ -203,6 +224,8 @@ options:
203
224
  --wsdl WSDL Custom WSDL directory path
204
225
  --cache {all,db,mem,none}
205
226
  Caching mode for ONVIFClient (default: all). 'all': memory+disk, 'db': disk-only, 'mem': memory-only, 'none': disabled.
227
+ --health-check-interval HEALTH_CHECK_INTERVAL, -hci HEALTH_CHECK_INTERVAL
228
+ Health check interval in seconds for interactive mode (default: 10)
206
229
  --version, -v Show ONVIF CLI version and exit
207
230
 
208
231
  Examples:
@@ -232,7 +255,7 @@ Examples:
232
255
  <summary><b>2. Interactive Shell</b></summary>
233
256
 
234
257
  ```bash
235
- ONVIF Interactive Shell Commands - v0.1.4
258
+ ONVIF Interactive Shell Commands - v0.1.6
236
259
  https://github.com/nirsimetri/onvif-python
237
260
 
238
261
  Basic Commands:
@@ -244,6 +267,7 @@ Basic Commands:
244
267
 
245
268
  Navigation Commands:
246
269
  <service> - Enter service mode (e.g., devicemgmt, media)
270
+ <service> <argument> - Enter service mode with argument (e.g. pullpoint SubscriptionRef=<value>)
247
271
  cd <service> - Enter service mode (alias)
248
272
  ls - List commands/services/methods in grid format
249
273
  up - Exit current service mode (go up one level)
@@ -263,6 +287,7 @@ Method Execution:
263
287
  Data Management:
264
288
  store <name> - Store last result with a name
265
289
  show <name> - Show stored data
290
+ show <name>[0] - Show element at index (for lists)
266
291
  show <name>.attribute - Show specific attribute
267
292
  show - List all stored data
268
293
  rm <name> - Remove stored data by name
@@ -330,6 +355,27 @@ If you omit the username or password, you will be prompted to enter them securel
330
355
  > [!IMPORTANT]
331
356
  > You can see all the other commands available in the interactive shell by trying it out directly. The interactive shell runs periodic background health checks to detect connection loss. It uses silent TCP pings to avoid interrupting your work and will automatically exit if the device is unreachable, similar to an SSH session.
332
357
 
358
+ **Command Chaining with `&&`:**
359
+
360
+ The CLI supports chaining multiple commands in a single line using the `&&` operator, allowing you to execute sequential operations efficiently:
361
+
362
+ ```bash
363
+ # Enter service and execute method in one line
364
+ 192.168.1.17:8000 > media && GetProfiles && store profiles
365
+
366
+ # Chain multiple method calls
367
+ 192.168.1.17:8000 > devicemgmt && GetDeviceInformation && store device_info
368
+
369
+ # Complex workflow
370
+ 192.168.1.17:8000 > media && GetProfiles && store profiles && up && imaging && GetImagingSettings VideoSourceToken=$profiles[0].VideoSourceConfiguration.SourceToken
371
+ ```
372
+
373
+ This feature is particularly useful for:
374
+ - Quick operations without entering service mode
375
+ - Scripting repetitive tasks
376
+ - Testing workflows
377
+ - Automating multi-step procedures
378
+
333
379
  **2. Device Discovery (WS-Discovery)**
334
380
 
335
381
  The CLI includes automatic ONVIF device discovery using the WS-Discovery protocol. This feature allows you to find all ONVIF-compliant devices on your local network without knowing their IP addresses beforehand.
@@ -453,7 +499,7 @@ The `ONVIFClient` class provides various configuration options to customize the
453
499
 
454
500
  | Parameter | Type | Required | Default | Description |
455
501
  |-----------|------|----------|---------|-------------|
456
- | `cache` | `CacheMode` | ❌ No | `CacheMode.ALL` | WSDL caching strategy (see [Cache Modes](#cache-modes) below) |
502
+ | `cache` | `CacheMode` | ❌ No | `CacheMode.ALL` | WSDL caching strategy (see **Cache Modes** below) |
457
503
 
458
504
  </details>
459
505
 
@@ -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
  ]
@@ -18,6 +18,7 @@ from ..utils.exceptions import ONVIFOperationException
18
18
  from .utils import (
19
19
  parse_json_params,
20
20
  get_service_methods,
21
+ get_service_required_args,
21
22
  get_device_available_services,
22
23
  colorize,
23
24
  format_capabilities_as_services,
@@ -172,6 +173,13 @@ class InteractiveShell(cmd.Cmd):
172
173
  if hasattr(args, "no_patch") and args.no_patch:
173
174
  options_info.append(f" ZeepPatcher : {colorize('Disabled', 'red')}")
174
175
 
176
+ if (
177
+ hasattr(args, "health_check_interval") and args.health_check_interval != 10
178
+ ): # 10 is default
179
+ options_info.append(
180
+ f" Health Check : every {colorize(f'{args.health_check_interval}s', 'yellow')}"
181
+ )
182
+
175
183
  # Format options info
176
184
  options_display = ""
177
185
  if options_info:
@@ -183,7 +191,7 @@ class InteractiveShell(cmd.Cmd):
183
191
  " / __ \\/ | / / | / / _/ ____/",
184
192
  " / / / / |/ /| | / // // /_ ",
185
193
  "/ /_/ / /| / | |/ // // __/ ",
186
- "\\____/_/ |_/ |___/___/_/ v0.1.4",
194
+ "\\____/_/ |_/ |___/___/_/ v0.1.6",
187
195
  " ",
188
196
  ]
189
197
 
@@ -223,30 +231,36 @@ class InteractiveShell(cmd.Cmd):
223
231
 
224
232
  def _periodic_health_check(self):
225
233
  """Periodically checks device connection using TCP or TLS depending on mode."""
234
+ # Get health check interval from args, default to 10 seconds
235
+ health_check_interval = getattr(self.args, "health_check_interval", 10)
236
+
226
237
  # Wait before first check to allow intro to finish
227
- self._stop_health_check.wait(10.0)
238
+ self._stop_health_check.wait(health_check_interval)
228
239
 
229
240
  while not self._stop_health_check.is_set():
230
241
  sock = None
231
242
  try:
232
- sock = socket.create_connection(
233
- (self.args.host, self.args.port), timeout=5.0
234
- )
235
-
236
- # If HTTPS, perform a short TLS handshake check
243
+ # For HTTPS, use ssl.create_connection for proper TLS handling
237
244
  if getattr(self.args, "https", False):
238
245
  context = ssl.create_default_context()
246
+ context.minimum_version = ssl.TLSVersion.TLSv1_2
239
247
  context.check_hostname = False
240
248
  context.verify_mode = ssl.CERT_NONE
241
249
 
242
- with context.wrap_socket(
243
- sock, server_hostname=self.args.host
244
- ) as ssock:
245
- ssock.settimeout(5)
246
- # Try a minimal handshake
247
- 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
+ )
248
262
 
249
- # If we reach here, the connection (and TLS if applicable) succeeded
263
+ # Connection successful
250
264
  pass
251
265
 
252
266
  except (
@@ -279,7 +293,7 @@ class InteractiveShell(cmd.Cmd):
279
293
  sock.close()
280
294
 
281
295
  # Wait before next check or stop signal
282
- self._stop_health_check.wait(5.0) # Check every 5 seconds
296
+ self._stop_health_check.wait(health_check_interval)
283
297
 
284
298
  def _handle_connection_error(self, e):
285
299
  """Handle connection errors by notifying the user and exiting."""
@@ -356,7 +370,7 @@ class InteractiveShell(cmd.Cmd):
356
370
 
357
371
  return parts
358
372
 
359
- def _display_grid(self, items, force_recalc=False):
373
+ def _display_grid(self, items):
360
374
  """Display items in grid format matching TAB completion (vertical layout)"""
361
375
  if not items:
362
376
  return
@@ -381,7 +395,7 @@ class InteractiveShell(cmd.Cmd):
381
395
 
382
396
  # Column width calculation: use same algorithm as cmd.Cmd.columnize()
383
397
  # Python's columnize uses: maxlen + 2 if maxlen > 0 else 2
384
- col_width = longest_length + 1 if longest_length > 0 else 2
398
+ col_width = longest_length + 2 if longest_length > 0 else 2
385
399
 
386
400
  # Calculate number of columns that fit
387
401
  num_cols = max(1, term_width // col_width)
@@ -549,8 +563,17 @@ class InteractiveShell(cmd.Cmd):
549
563
 
550
564
  def default(self, line):
551
565
  """Handle unknown commands"""
552
- # Check if it's a service call using device-specific services
553
- if line in get_device_available_services(self.client):
566
+ available_services = get_device_available_services(self.client)
567
+
568
+ # Check if it's a service call (with or without arguments)
569
+ # Extract first word to check if it's a service name
570
+ first_word = line.split()[0] if line.split() else line
571
+
572
+ if first_word in available_services:
573
+ return self.do_enter_service(line)
574
+
575
+ # Check if the whole line (without arguments) is a service
576
+ if line in available_services:
554
577
  return self.do_enter_service(line)
555
578
 
556
579
  # Check if it's a method call in service context
@@ -640,28 +663,17 @@ class InteractiveShell(cmd.Cmd):
640
663
  print("^C")
641
664
  line = ""
642
665
 
643
- # Handle TAB completion on empty line
644
- if line == "\t":
645
- print() # Newline before grid
646
- if self.current_service:
647
- self._display_grid(get_service_methods(self.current_service))
648
- else:
649
- services = get_device_available_services(self.client)
650
- commands = self.base_commands
651
- self._display_grid(services + commands)
652
- line = "" # Reset line to show prompt again
653
-
654
666
  line = self.precmd(line)
655
667
  stop = self.onecmd(line)
656
668
  stop = self.postcmd(stop, line)
657
669
 
658
- def columnize(self, list, displaywidth=80):
670
+ def columnize(self, items, displaywidth=80):
659
671
  """Override columnize to use grid format for TAB completion"""
660
- if not list:
672
+ if not items:
661
673
  return
662
674
 
663
675
  # Use our grid display for TAB completion with coloring enabled
664
- self._display_grid(list, force_recalc=True)
676
+ self._display_grid(items)
665
677
 
666
678
  def print_topics(self, header, cmds, cmdlen, maxcol):
667
679
  """Override print_topics to use grid format for TAB completion"""
@@ -673,7 +685,7 @@ class InteractiveShell(cmd.Cmd):
673
685
  self.stdout.write("%s\n" % str(header))
674
686
 
675
687
  # Use our grid display for TAB completion with coloring enabled
676
- self._display_grid(cmds, force_recalc=True)
688
+ self._display_grid(cmds)
677
689
 
678
690
  def do_capabilities(self, line):
679
691
  """Show device capabilities in service format"""
@@ -721,8 +733,13 @@ class InteractiveShell(cmd.Cmd):
721
733
  else:
722
734
  print(f"{colorize('Error:', 'red')} {e}")
723
735
 
724
- def do_enter_service(self, service_name):
725
- """Enter service mode"""
736
+ def do_enter_service(self, line):
737
+ """Enter service mode with optional arguments for services that require them"""
738
+ # Parse service name and arguments
739
+ parts = line.split(None, 1)
740
+ service_name = parts[0] if parts else ""
741
+ args_str = parts[1] if len(parts) > 1 else ""
742
+
726
743
  available_services = get_device_available_services(self.client)
727
744
  if service_name not in available_services:
728
745
  print(f"{colorize('Error:', 'red')} Unknown service '{service_name}'")
@@ -731,7 +748,63 @@ class InteractiveShell(cmd.Cmd):
731
748
  return
732
749
 
733
750
  try:
734
- service = getattr(self.client, service_name)()
751
+ # Check if this service requires arguments
752
+ required_args = get_service_required_args(service_name)
753
+
754
+ if required_args:
755
+ # Service requires arguments
756
+ if not args_str:
757
+ # No arguments provided, show help
758
+ print(
759
+ f"{colorize('Error:', 'red')} Service '{service_name}' requires arguments"
760
+ )
761
+ print(
762
+ f"{colorize('Usage:', 'yellow')} {service_name} {' '.join([f'{arg}=<value>' for arg in required_args])}"
763
+ )
764
+ print(
765
+ f"{colorize('Example:', 'yellow')} {service_name} {required_args[0]}=$subscription"
766
+ )
767
+ return
768
+
769
+ # Parse arguments
770
+ try:
771
+ parsed_args = parse_json_params(args_str)
772
+ except Exception as e:
773
+ print(f"{colorize('Error parsing arguments:', 'red')} {e}")
774
+ return
775
+
776
+ # Check if all required arguments are provided
777
+ missing_args = [arg for arg in required_args if arg not in parsed_args]
778
+ if missing_args:
779
+ print(
780
+ f"{colorize('Error:', 'red')} Missing required arguments: {', '.join(missing_args)}"
781
+ )
782
+ print(
783
+ f"{colorize('Usage:', 'yellow')} {service_name} {' '.join([f'{arg}=<value>' for arg in required_args])}"
784
+ )
785
+ return
786
+
787
+ # Resolve stored references in arguments
788
+ for arg_name in required_args:
789
+ arg_value = parsed_args[arg_name]
790
+ if isinstance(arg_value, str) and arg_value.startswith("$"):
791
+ # Resolve stored reference
792
+ reference = arg_value[1:] # Remove $ prefix
793
+ resolved_value = self._resolve_stored_reference(reference)
794
+ if resolved_value is None:
795
+ print(
796
+ f"{colorize('Error:', 'red')} Could not resolve reference '{arg_value}'"
797
+ )
798
+ return
799
+ parsed_args[arg_name] = resolved_value
800
+
801
+ # Call service method with arguments
802
+ service_method = getattr(self.client, service_name)
803
+ service = service_method(**parsed_args)
804
+ else:
805
+ # Service doesn't require arguments
806
+ service = getattr(self.client, service_name)()
807
+
735
808
  self.current_service = service
736
809
  self.current_service_name = service_name
737
810
  self.update_prompt()
@@ -756,6 +829,10 @@ class InteractiveShell(cmd.Cmd):
756
829
 
757
830
  except Exception as e:
758
831
  print(f"{colorize('Error entering service:', 'red')} {e}")
832
+ if self.args.debug:
833
+ import traceback
834
+
835
+ traceback.print_exc()
759
836
 
760
837
  def do_ls(self, line):
761
838
  """List available commands/services like TAB completion"""
@@ -843,11 +920,11 @@ class InteractiveShell(cmd.Cmd):
843
920
  print(
844
921
  f"{colorize('Error:', 'red')} You must be in a service mode to use 'type'."
845
922
  )
846
- print(f"Enter a service first (e.g., {colorize('d', 'yellow')})")
923
+ print(f"Enter a service first (e.g., {colorize('devicemgmt', 'yellow')})")
847
924
  return
848
925
 
849
926
  if not operation_name:
850
- print(f"Usage: {colorize('type <operation_name>', 'yellow')}")
927
+ print("Usage: type <operation_name>")
851
928
  return
852
929
 
853
930
  if not hasattr(self.current_service, operation_name):
@@ -1191,6 +1268,14 @@ class InteractiveShell(cmd.Cmd):
1191
1268
  if hasattr(self.args, "no_patch") and self.args.no_patch:
1192
1269
  options_info.append(f" ZeepPatcher : {colorize('Disabled', 'red')}")
1193
1270
 
1271
+ if (
1272
+ hasattr(self.args, "health_check_interval")
1273
+ and self.args.health_check_interval != 10
1274
+ ): # 10 is default
1275
+ options_info.append(
1276
+ f" Health Check : every {colorize(f'{self.args.health_check_interval}s', 'yellow')}"
1277
+ )
1278
+
1194
1279
  # Format options info
1195
1280
  options_display = ""
1196
1281
  if options_info:
@@ -1300,7 +1385,7 @@ class InteractiveShell(cmd.Cmd):
1300
1385
  super().do_help(line)
1301
1386
  else:
1302
1387
  help_text = f"""
1303
- {colorize('ONVIF Interactive Shell Commands - v0.1.4', 'cyan')}\n{colorize('https://github.com/nirsimetri/onvif-python', 'white')}
1388
+ {colorize('ONVIF Interactive Shell Commands - v0.1.6', 'cyan')}\n{colorize('https://github.com/nirsimetri/onvif-python', 'white')}
1304
1389
 
1305
1390
  {colorize('Basic Commands:', 'yellow')}
1306
1391
  capabilities, caps - Show device capabilities
@@ -1311,6 +1396,7 @@ class InteractiveShell(cmd.Cmd):
1311
1396
 
1312
1397
  {colorize('Navigation Commands:', 'yellow')}
1313
1398
  <service> - Enter service mode (e.g., devicemgmt, media)
1399
+ <service> <argument> - Enter service mode with argument (e.g. pullpoint SubscriptionRef=<value>)
1314
1400
  cd <service> - Enter service mode (alias)
1315
1401
  ls - List commands/services/methods in grid format
1316
1402
  up - Exit current service mode (go up one level)