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.
- {onvif_python-0.1.4/onvif_python.egg-info → onvif_python-0.1.6}/PKG-INFO +58 -12
- {onvif_python-0.1.4 → onvif_python-0.1.6}/README.md +57 -11
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/__init__.py +8 -1
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/cli/interactive.py +126 -40
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/cli/main.py +20 -163
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/cli/utils.py +205 -51
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/client.py +4 -4
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/utils/__init__.py +2 -0
- onvif_python-0.1.6/onvif/utils/discovery.py +297 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6/onvif_python.egg-info}/PKG-INFO +58 -12
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif_python.egg-info/SOURCES.txt +1 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/pyproject.toml +1 -1
- {onvif_python-0.1.4 → onvif_python-0.1.6}/LICENSE.md +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/MANIFEST.in +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/cli/__init__.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/cli/__main__.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/operator.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/__init__.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/accesscontrol.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/accessrules.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/actionengine.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/analytics/analytics.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/analytics/ruleengine.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/analyticsdevice.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/appmgmt.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/authenticationbehavior.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/credential.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/deviceio.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/devicemgmt.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/display.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/doorcontrol.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/events/events.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/events/notification.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/events/pullpoint.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/events/subscription.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/imaging.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/media.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/media2.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/provisioning.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/ptz.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/receiver.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/recording.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/replay.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/schedule.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/search.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/security/advancedsecurity.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/security/authorizationserver.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/security/dot1x.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/security/jwt.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/security/keystore.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/security/mediasigning.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/security/tlsserver.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/thermal.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/services/uplink.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/utils/error_handlers.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/utils/exceptions.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/utils/wsdl.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/utils/xml_capture.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/utils/zeep.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/display/display.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/display.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/receiver.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/recording.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/replay.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/schema/common.xsd +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/search.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/analytics/index.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif_python.egg-info/dependency_links.txt +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif_python.egg-info/entry_points.txt +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif_python.egg-info/requires.txt +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/onvif_python.egg-info/top_level.txt +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/setup.cfg +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/setup.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/tests/test_client.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/tests/test_core.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/tests/test_error_handlers.py +0 -0
- {onvif_python-0.1.4 → onvif_python-0.1.6}/tests/test_exceptions.py +0 -0
- {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.
|
|
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
|
-
[](https://app.codacy.com/gh/nirsimetri/onvif-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
|
42
42
|
[](https://deepwiki.com/nirsimetri/onvif-python)
|
|
43
|
-
[](https://pypi.org/project/onvif-python/)
|
|
44
44
|
[](https://clickpy.clickhouse.com/dashboard/onvif-python)
|
|
45
45
|
<br>
|
|
46
46
|
[](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
|
|
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.
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
[](https://app.codacy.com/gh/nirsimetri/onvif-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
|
4
4
|
[](https://deepwiki.com/nirsimetri/onvif-python)
|
|
5
|
-
[](https://pypi.org/project/onvif-python/)
|
|
6
6
|
[](https://clickpy.clickhouse.com/dashboard/onvif-python)
|
|
7
7
|
<br>
|
|
8
8
|
[](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
|
|
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.
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
#
|
|
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(
|
|
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
|
|
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 +
|
|
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
|
-
|
|
553
|
-
|
|
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,
|
|
670
|
+
def columnize(self, items, displaywidth=80):
|
|
659
671
|
"""Override columnize to use grid format for TAB completion"""
|
|
660
|
-
if not
|
|
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(
|
|
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
|
|
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,
|
|
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
|
|
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('
|
|
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(
|
|
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.
|
|
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)
|