onvif-python 0.1.6__tar.gz → 0.1.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {onvif_python-0.1.6/onvif_python.egg-info → onvif_python-0.1.8}/PKG-INFO +40 -11
- {onvif_python-0.1.6 → onvif_python-0.1.8}/README.md +32 -8
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/cli/interactive.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/cli/main.py +34 -20
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/cli/utils.py +38 -13
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/client.py +101 -21
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/operator.py +50 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/accesscontrol.py +13 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/accessrules.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/actionengine.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/analytics/analytics.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/analytics/ruleengine.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/analyticsdevice.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/appmgmt.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/authenticationbehavior.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/credential.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/deviceio.py +4 -4
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/devicemgmt.py +4 -4
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/display.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/doorcontrol.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/events/events.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/events/notification.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/events/pullpoint.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/events/subscription.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/imaging.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/media.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/media2.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/provisioning.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/ptz.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/receiver.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/recording.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/replay.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/schedule.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/search.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/security/advancedsecurity.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/security/authorizationserver.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/security/dot1x.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/security/jwt.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/security/keystore.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/security/mediasigning.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/security/tlsserver.py +3 -3
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/thermal.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/uplink.py +2 -2
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/utils/__init__.py +2 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/utils/discovery.py +96 -22
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/utils/error_handlers.py +31 -4
- onvif_python-0.1.8/onvif/utils/exceptions.py +163 -0
- onvif_python-0.1.8/onvif/utils/service.py +108 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/utils/wsdl.py +144 -16
- onvif_python-0.1.8/onvif/utils/xml_capture.py +195 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/utils/zeep.py +144 -20
- {onvif_python-0.1.6 → onvif_python-0.1.8/onvif_python.egg-info}/PKG-INFO +40 -11
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif_python.egg-info/SOURCES.txt +1 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/pyproject.toml +8 -3
- {onvif_python-0.1.6 → onvif_python-0.1.8}/tests/test_client.py +3 -1
- onvif_python-0.1.6/onvif/utils/exceptions.py +0 -91
- onvif_python-0.1.6/onvif/utils/xml_capture.py +0 -145
- {onvif_python-0.1.6 → onvif_python-0.1.8}/LICENSE.md +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/MANIFEST.in +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/__init__.py +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/cli/__init__.py +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/cli/__main__.py +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/services/__init__.py +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/display/display.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/display.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/receiver.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/recording.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/replay.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/schema/common.xsd +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/search.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/index.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif_python.egg-info/dependency_links.txt +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif_python.egg-info/entry_points.txt +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif_python.egg-info/requires.txt +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/onvif_python.egg-info/top_level.txt +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/setup.cfg +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/setup.py +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/tests/test_core.py +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/tests/test_error_handlers.py +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/tests/test_exceptions.py +0 -0
- {onvif_python-0.1.6 → onvif_python-0.1.8}/tests/test_wsdl_map.py +0 -0
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: onvif-python
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: A
|
|
3
|
+
Version: 0.1.8
|
|
4
|
+
Summary: A modern Python library for ONVIF-compliant devices
|
|
5
5
|
Author-email: Nirsimetri Technologies® <open@nirsimetri.com>
|
|
6
6
|
Maintainer: Kaburagi
|
|
7
7
|
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/nirsimetri/onvif-python
|
|
8
9
|
Project-URL: Documentation, https://deepwiki.com/nirsimetri/onvif-python
|
|
9
10
|
Project-URL: Changelog, https://github.com/nirsimetri/onvif-python/releases
|
|
10
11
|
Keywords: onvif,Camera,onvif-python,ip-camera,cctv,soap,surveillance,cctv cameras,soap-client,onvif-client,onvif-library,onvif-specifications
|
|
11
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
13
|
Classifier: Environment :: Console
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
20
|
Classifier: Operating System :: OS Independent
|
|
16
21
|
Classifier: Intended Audience :: Developers
|
|
17
22
|
Classifier: Intended Audience :: Information Technology
|
|
@@ -36,15 +41,23 @@ Requires-Dist: black; extra == "dev"
|
|
|
36
41
|
Requires-Dist: flake8; extra == "dev"
|
|
37
42
|
Dynamic: license-file
|
|
38
43
|
|
|
39
|
-
|
|
44
|
+
<h1 align="center">ONVIF Python</h1>
|
|
40
45
|
|
|
41
|
-
|
|
46
|
+
<div align="center">
|
|
47
|
+
|
|
48
|
+
[](https://app.codacy.com/gh/nirsimetri/onvif-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
|
42
49
|
[](https://deepwiki.com/nirsimetri/onvif-python)
|
|
43
|
-
[](https://pypi.org/project/onvif-python/)
|
|
44
51
|
[](https://clickpy.clickhouse.com/dashboard/onvif-python)
|
|
45
52
|
<br>
|
|
46
53
|
[](https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml)
|
|
47
54
|
[](https://github.com/nirsimetri/onvif-python/actions/workflows/python-publish.yml)
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<h1 align="center">
|
|
58
|
+
<img src="https://raw.githubusercontent.com/nirsimetri/onvif-python/refs/heads/main/assets/carbon_onvif.png" alt="onvif" width="700px">
|
|
59
|
+
<br>
|
|
60
|
+
</h1>
|
|
48
61
|
|
|
49
62
|
**This project provides a comprehensive and developer-friendly Python library for working with ONVIF-compliant devices.** It is designed to be reliable, easy to integrate, and flexible enough to support a wide range of ONVIF profiles and services.
|
|
50
63
|
|
|
@@ -107,6 +120,10 @@ discovery = ONVIFDiscovery(timeout=5)
|
|
|
107
120
|
# Discover devices
|
|
108
121
|
devices = discovery.discover()
|
|
109
122
|
|
|
123
|
+
# Or with
|
|
124
|
+
# Discover with search filter by types or scopes (case-insensitive substring match)
|
|
125
|
+
devices = discovery.discover(search="Profile/Streaming")
|
|
126
|
+
|
|
110
127
|
# Display discovered devices
|
|
111
128
|
for device in devices:
|
|
112
129
|
print(f"Found device at {device['host']}:{device['port']}")
|
|
@@ -182,7 +199,7 @@ Explore more advanced usage and service-specific operations in the [`examples/`]
|
|
|
182
199
|
## ONVIF CLI
|
|
183
200
|
|
|
184
201
|
> [!NOTE]
|
|
185
|
-
> The CLI is automatically installed when you install the `onvif-python` see [Installation](#installation). This feature has been available since `onvif-python` version [
|
|
202
|
+
> The CLI is automatically installed when you install the `onvif-python` see [Installation](#installation). This feature has been available since `onvif-python` version [`>=0.1.1`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.1.1).
|
|
186
203
|
|
|
187
204
|

|
|
188
205
|

|
|
@@ -232,11 +249,12 @@ This library includes a powerful command-line interface (CLI) for interacting wi
|
|
|
232
249
|
<summary><b>1. Direct CLI</b></summary>
|
|
233
250
|
|
|
234
251
|
```bash
|
|
235
|
-
usage: onvif [-h] [--host HOST] [--port PORT] [--username USERNAME] [--password PASSWORD] [--discover] [--
|
|
236
|
-
[--no-patch] [--interactive] [--debug] [--wsdl WSDL] [--cache {all,db,mem,none}]
|
|
252
|
+
usage: onvif [-h] [--host HOST] [--port PORT] [--username USERNAME] [--password PASSWORD] [--discover] [--search SEARCH] [--timeout TIMEOUT] [--https]
|
|
253
|
+
[--no-verify] [--no-patch] [--interactive] [--debug] [--wsdl WSDL] [--cache {all,db,mem,none}]
|
|
254
|
+
[--health-check-interval HEALTH_CHECK_INTERVAL] [--version]
|
|
237
255
|
[service] [method] [params ...]
|
|
238
256
|
|
|
239
|
-
ONVIF Terminal Client — v0.1.
|
|
257
|
+
ONVIF Terminal Client — v0.1.8
|
|
240
258
|
https://github.com/nirsimetri/onvif-python
|
|
241
259
|
|
|
242
260
|
positional arguments:
|
|
@@ -253,6 +271,8 @@ options:
|
|
|
253
271
|
--password PASSWORD, -p PASSWORD
|
|
254
272
|
Password for authentication
|
|
255
273
|
--discover, -d Discover ONVIF devices on the network using WS-Discovery
|
|
274
|
+
--search SEARCH, -s SEARCH
|
|
275
|
+
Filter discovered devices by types or scopes (case-insensitive substring match)
|
|
256
276
|
--timeout TIMEOUT Connection timeout in seconds (default: 10)
|
|
257
277
|
--https Use HTTPS instead of HTTP
|
|
258
278
|
--no-verify Disable SSL certificate verification
|
|
@@ -272,6 +292,11 @@ Examples:
|
|
|
272
292
|
onvif media GetProfiles --discover --username admin
|
|
273
293
|
onvif -d -i
|
|
274
294
|
|
|
295
|
+
# Discover with filtering
|
|
296
|
+
onvif --discover --search ptz --interactive
|
|
297
|
+
onvif -d -s "C210" -i
|
|
298
|
+
onvif -d -s "audio_encoder" -u admin -p admin123 -i
|
|
299
|
+
|
|
275
300
|
# Direct command execution
|
|
276
301
|
onvif devicemgmt GetCapabilities Category=All --host 192.168.1.17 --port 8000 --username admin --password admin123
|
|
277
302
|
onvif ptz ContinuousMove ProfileToken=Profile_1 Velocity={"PanTilt": {"x": -0.1, "y": 0}} --host 192.168.1.17 --port 8000 --username admin --password admin123
|
|
@@ -293,7 +318,7 @@ Examples:
|
|
|
293
318
|
<summary><b>2. Interactive Shell</b></summary>
|
|
294
319
|
|
|
295
320
|
```bash
|
|
296
|
-
ONVIF Interactive Shell
|
|
321
|
+
ONVIF Interactive Shell — v0.1.8
|
|
297
322
|
https://github.com/nirsimetri/onvif-python
|
|
298
323
|
|
|
299
324
|
Basic Commands:
|
|
@@ -426,6 +451,10 @@ onvif --discover --username admin --password admin123 --interactive
|
|
|
426
451
|
# Short form
|
|
427
452
|
onvif -d -u admin -p admin123 -i
|
|
428
453
|
|
|
454
|
+
# Discover with search filter
|
|
455
|
+
onvif --discover --search "C210" --interactive
|
|
456
|
+
onvif -d -s ptz -u admin -p admin123 -i
|
|
457
|
+
|
|
429
458
|
# Discover and interactive (will prompt for credentials)
|
|
430
459
|
onvif -d -i
|
|
431
460
|
```
|
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center">ONVIF Python</h1>
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
[](https://app.codacy.com/gh/nirsimetri/onvif-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
|
4
6
|
[](https://deepwiki.com/nirsimetri/onvif-python)
|
|
5
|
-
[](https://pypi.org/project/onvif-python/)
|
|
6
8
|
[](https://clickpy.clickhouse.com/dashboard/onvif-python)
|
|
7
9
|
<br>
|
|
8
10
|
[](https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml)
|
|
9
11
|
[](https://github.com/nirsimetri/onvif-python/actions/workflows/python-publish.yml)
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<h1 align="center">
|
|
15
|
+
<img src="https://raw.githubusercontent.com/nirsimetri/onvif-python/refs/heads/main/assets/carbon_onvif.png" alt="onvif" width="700px">
|
|
16
|
+
<br>
|
|
17
|
+
</h1>
|
|
10
18
|
|
|
11
19
|
**This project provides a comprehensive and developer-friendly Python library for working with ONVIF-compliant devices.** It is designed to be reliable, easy to integrate, and flexible enough to support a wide range of ONVIF profiles and services.
|
|
12
20
|
|
|
@@ -69,6 +77,10 @@ discovery = ONVIFDiscovery(timeout=5)
|
|
|
69
77
|
# Discover devices
|
|
70
78
|
devices = discovery.discover()
|
|
71
79
|
|
|
80
|
+
# Or with
|
|
81
|
+
# Discover with search filter by types or scopes (case-insensitive substring match)
|
|
82
|
+
devices = discovery.discover(search="Profile/Streaming")
|
|
83
|
+
|
|
72
84
|
# Display discovered devices
|
|
73
85
|
for device in devices:
|
|
74
86
|
print(f"Found device at {device['host']}:{device['port']}")
|
|
@@ -144,7 +156,7 @@ Explore more advanced usage and service-specific operations in the [`examples/`]
|
|
|
144
156
|
## ONVIF CLI
|
|
145
157
|
|
|
146
158
|
> [!NOTE]
|
|
147
|
-
> The CLI is automatically installed when you install the `onvif-python` see [Installation](#installation). This feature has been available since `onvif-python` version [
|
|
159
|
+
> The CLI is automatically installed when you install the `onvif-python` see [Installation](#installation). This feature has been available since `onvif-python` version [`>=0.1.1`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.1.1).
|
|
148
160
|
|
|
149
161
|

|
|
150
162
|

|
|
@@ -194,11 +206,12 @@ This library includes a powerful command-line interface (CLI) for interacting wi
|
|
|
194
206
|
<summary><b>1. Direct CLI</b></summary>
|
|
195
207
|
|
|
196
208
|
```bash
|
|
197
|
-
usage: onvif [-h] [--host HOST] [--port PORT] [--username USERNAME] [--password PASSWORD] [--discover] [--
|
|
198
|
-
[--no-patch] [--interactive] [--debug] [--wsdl WSDL] [--cache {all,db,mem,none}]
|
|
209
|
+
usage: onvif [-h] [--host HOST] [--port PORT] [--username USERNAME] [--password PASSWORD] [--discover] [--search SEARCH] [--timeout TIMEOUT] [--https]
|
|
210
|
+
[--no-verify] [--no-patch] [--interactive] [--debug] [--wsdl WSDL] [--cache {all,db,mem,none}]
|
|
211
|
+
[--health-check-interval HEALTH_CHECK_INTERVAL] [--version]
|
|
199
212
|
[service] [method] [params ...]
|
|
200
213
|
|
|
201
|
-
ONVIF Terminal Client — v0.1.
|
|
214
|
+
ONVIF Terminal Client — v0.1.8
|
|
202
215
|
https://github.com/nirsimetri/onvif-python
|
|
203
216
|
|
|
204
217
|
positional arguments:
|
|
@@ -215,6 +228,8 @@ options:
|
|
|
215
228
|
--password PASSWORD, -p PASSWORD
|
|
216
229
|
Password for authentication
|
|
217
230
|
--discover, -d Discover ONVIF devices on the network using WS-Discovery
|
|
231
|
+
--search SEARCH, -s SEARCH
|
|
232
|
+
Filter discovered devices by types or scopes (case-insensitive substring match)
|
|
218
233
|
--timeout TIMEOUT Connection timeout in seconds (default: 10)
|
|
219
234
|
--https Use HTTPS instead of HTTP
|
|
220
235
|
--no-verify Disable SSL certificate verification
|
|
@@ -234,6 +249,11 @@ Examples:
|
|
|
234
249
|
onvif media GetProfiles --discover --username admin
|
|
235
250
|
onvif -d -i
|
|
236
251
|
|
|
252
|
+
# Discover with filtering
|
|
253
|
+
onvif --discover --search ptz --interactive
|
|
254
|
+
onvif -d -s "C210" -i
|
|
255
|
+
onvif -d -s "audio_encoder" -u admin -p admin123 -i
|
|
256
|
+
|
|
237
257
|
# Direct command execution
|
|
238
258
|
onvif devicemgmt GetCapabilities Category=All --host 192.168.1.17 --port 8000 --username admin --password admin123
|
|
239
259
|
onvif ptz ContinuousMove ProfileToken=Profile_1 Velocity={"PanTilt": {"x": -0.1, "y": 0}} --host 192.168.1.17 --port 8000 --username admin --password admin123
|
|
@@ -255,7 +275,7 @@ Examples:
|
|
|
255
275
|
<summary><b>2. Interactive Shell</b></summary>
|
|
256
276
|
|
|
257
277
|
```bash
|
|
258
|
-
ONVIF Interactive Shell
|
|
278
|
+
ONVIF Interactive Shell — v0.1.8
|
|
259
279
|
https://github.com/nirsimetri/onvif-python
|
|
260
280
|
|
|
261
281
|
Basic Commands:
|
|
@@ -388,6 +408,10 @@ onvif --discover --username admin --password admin123 --interactive
|
|
|
388
408
|
# Short form
|
|
389
409
|
onvif -d -u admin -p admin123 -i
|
|
390
410
|
|
|
411
|
+
# Discover with search filter
|
|
412
|
+
onvif --discover --search "C210" --interactive
|
|
413
|
+
onvif -d -s ptz -u admin -p admin123 -i
|
|
414
|
+
|
|
391
415
|
# Discover and interactive (will prompt for credentials)
|
|
392
416
|
onvif -d -i
|
|
393
417
|
```
|
|
@@ -191,7 +191,7 @@ class InteractiveShell(cmd.Cmd):
|
|
|
191
191
|
" / __ \\/ | / / | / / _/ ____/",
|
|
192
192
|
" / / / / |/ /| | / // // /_ ",
|
|
193
193
|
"/ /_/ / /| / | |/ // // __/ ",
|
|
194
|
-
"\\____/_/ |_/ |___/___/_/ v0.1.
|
|
194
|
+
"\\____/_/ |_/ |___/___/_/ v0.1.8",
|
|
195
195
|
" ",
|
|
196
196
|
]
|
|
197
197
|
|
|
@@ -1385,7 +1385,7 @@ class InteractiveShell(cmd.Cmd):
|
|
|
1385
1385
|
super().do_help(line)
|
|
1386
1386
|
else:
|
|
1387
1387
|
help_text = f"""
|
|
1388
|
-
{colorize('ONVIF Interactive Shell
|
|
1388
|
+
{colorize('ONVIF Interactive Shell — v0.1.8', 'cyan')}\n{colorize('https://github.com/nirsimetri/onvif-python', 'white')}
|
|
1389
1389
|
|
|
1390
1390
|
{colorize('Basic Commands:', 'yellow')}
|
|
1391
1391
|
capabilities, caps - Show device capabilities
|
|
@@ -17,7 +17,7 @@ def create_parser():
|
|
|
17
17
|
"""Create argument parser for ONVIF CLI"""
|
|
18
18
|
parser = argparse.ArgumentParser(
|
|
19
19
|
prog="onvif",
|
|
20
|
-
description=f"{colorize('ONVIF Terminal Client', 'yellow')} — v0.1.
|
|
20
|
+
description=f"{colorize('ONVIF Terminal Client', 'yellow')} — v0.1.8\nhttps://github.com/nirsimetri/onvif-python",
|
|
21
21
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
22
22
|
epilog=f"""
|
|
23
23
|
Examples:
|
|
@@ -26,6 +26,11 @@ Examples:
|
|
|
26
26
|
{colorize('onvif', 'yellow')} media GetProfiles --discover --username admin
|
|
27
27
|
{colorize('onvif', 'yellow')} -d -i
|
|
28
28
|
|
|
29
|
+
# Discover with filtering
|
|
30
|
+
{colorize('onvif', 'yellow')} --discover --search ptz --interactive
|
|
31
|
+
{colorize('onvif', 'yellow')} -d -s "C210" -i
|
|
32
|
+
{colorize('onvif', 'yellow')} -d -s "audio_encoder" -u admin -p admin123 -i
|
|
33
|
+
|
|
29
34
|
# Direct command execution
|
|
30
35
|
{colorize('onvif', 'yellow')} devicemgmt GetCapabilities Category=All --host 192.168.1.17 --port 8000 --username admin --password admin123
|
|
31
36
|
{colorize('onvif', 'yellow')} ptz ContinuousMove ProfileToken=Profile_1 Velocity={{"PanTilt": {{"x": -0.1, "y": 0}}}} --host 192.168.1.17 --port 8000 --username admin --password admin123
|
|
@@ -61,6 +66,11 @@ Examples:
|
|
|
61
66
|
action="store_true",
|
|
62
67
|
help="Discover ONVIF devices on the network using WS-Discovery",
|
|
63
68
|
)
|
|
69
|
+
parser.add_argument(
|
|
70
|
+
"--search",
|
|
71
|
+
"-s",
|
|
72
|
+
help="Filter discovered devices by types or scopes (case-insensitive substring match)",
|
|
73
|
+
)
|
|
64
74
|
|
|
65
75
|
# Connection options
|
|
66
76
|
parser.add_argument(
|
|
@@ -139,7 +149,7 @@ def main():
|
|
|
139
149
|
|
|
140
150
|
# Show ONVIF CLI version
|
|
141
151
|
if args.version:
|
|
142
|
-
print(colorize("0.1.
|
|
152
|
+
print(colorize("0.1.8", "yellow"))
|
|
143
153
|
sys.exit(0)
|
|
144
154
|
|
|
145
155
|
# Validate arguments early (before discovery)
|
|
@@ -155,11 +165,18 @@ def main():
|
|
|
155
165
|
f"{colorize('--discover', 'white')} cannot be used with {colorize('--host', 'white')}"
|
|
156
166
|
)
|
|
157
167
|
|
|
158
|
-
# Discover devices (pass --https flag to prioritize HTTPS XAddrs)
|
|
159
|
-
devices = discover_devices(
|
|
168
|
+
# Discover devices (pass --https flag to prioritize HTTPS XAddrs and search term)
|
|
169
|
+
devices = discover_devices(
|
|
170
|
+
timeout=4, prefer_https=args.https, search=args.search
|
|
171
|
+
)
|
|
160
172
|
|
|
161
173
|
if not devices:
|
|
162
|
-
|
|
174
|
+
if args.search:
|
|
175
|
+
print(
|
|
176
|
+
f"{colorize('No devices found matching search:', 'red')} {colorize(args.search, 'white')}"
|
|
177
|
+
)
|
|
178
|
+
else:
|
|
179
|
+
print(colorize("No ONVIF devices discovered. Exiting.", "red"))
|
|
163
180
|
sys.exit(1)
|
|
164
181
|
|
|
165
182
|
# Let user select a device
|
|
@@ -278,34 +295,31 @@ def execute_command(
|
|
|
278
295
|
return method(**params)
|
|
279
296
|
|
|
280
297
|
|
|
281
|
-
def discover_devices(
|
|
298
|
+
def discover_devices(
|
|
299
|
+
timeout: int = 4, prefer_https: bool = False, search: Optional[str] = None
|
|
300
|
+
) -> list:
|
|
282
301
|
"""Discover ONVIF devices on the network using WS-Discovery.
|
|
283
302
|
|
|
284
303
|
Args:
|
|
285
304
|
timeout: Discovery timeout in seconds
|
|
286
305
|
prefer_https: If True, prioritize HTTPS XAddrs when available
|
|
306
|
+
search: Optional search term to filter devices by types or scopes
|
|
287
307
|
|
|
288
308
|
Returns:
|
|
289
309
|
List of discovered devices with connection info
|
|
290
310
|
"""
|
|
291
|
-
# Get local network interface for display
|
|
292
|
-
try:
|
|
293
|
-
import socket
|
|
294
311
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
local_ip = s.getsockname()[0]
|
|
298
|
-
s.close()
|
|
299
|
-
except Exception:
|
|
300
|
-
local_ip = "0.0.0.0"
|
|
312
|
+
# Use ONVIFDiscovery class
|
|
313
|
+
discovery = ONVIFDiscovery(timeout=timeout)
|
|
301
314
|
|
|
302
315
|
print(f"\n{colorize('Discovering ONVIF devices on network...', 'yellow')}")
|
|
303
|
-
print(f"Network interface: {colorize(
|
|
304
|
-
print(f"Timeout: {timeout}s
|
|
316
|
+
print(f"Network interface: {colorize(discovery._get_local_ip(), 'white')}")
|
|
317
|
+
print(f"Timeout: {timeout}s")
|
|
318
|
+
if search:
|
|
319
|
+
print(f"Filter: {colorize(search, 'yellow')}")
|
|
320
|
+
print()
|
|
305
321
|
|
|
306
|
-
|
|
307
|
-
discovery = ONVIFDiscovery(timeout=timeout)
|
|
308
|
-
devices = discovery.discover(prefer_https=prefer_https)
|
|
322
|
+
devices = discovery.discover(prefer_https=prefer_https, search=search)
|
|
309
323
|
|
|
310
324
|
return devices
|
|
311
325
|
|
|
@@ -4,7 +4,7 @@ import json
|
|
|
4
4
|
import os
|
|
5
5
|
import inspect
|
|
6
6
|
from typing import Any, Dict, Optional
|
|
7
|
-
import
|
|
7
|
+
from lxml import etree
|
|
8
8
|
import re
|
|
9
9
|
|
|
10
10
|
|
|
@@ -231,10 +231,17 @@ def get_method_documentation(service_obj, method_name: str) -> Optional[Dict[str
|
|
|
231
231
|
try:
|
|
232
232
|
# 1. Get documentation from WSDL (existing logic)
|
|
233
233
|
wsdl_path = service_obj.operator.wsdl_path
|
|
234
|
-
|
|
234
|
+
|
|
235
|
+
# Use secure lxml parser
|
|
236
|
+
parser = etree.XMLParser(
|
|
237
|
+
resolve_entities=False,
|
|
238
|
+
no_network=True,
|
|
239
|
+
remove_blank_text=True,
|
|
240
|
+
)
|
|
241
|
+
tree = etree.parse(wsdl_path, parser)
|
|
235
242
|
root = tree.getroot()
|
|
236
243
|
namespaces = {
|
|
237
|
-
node[0]: node[1] for node in
|
|
244
|
+
node[0]: node[1] for node in etree.iterparse(wsdl_path, events=["start-ns"])
|
|
238
245
|
}
|
|
239
246
|
namespaces["wsdl"] = "http://schemas.xmlsoap.org/wsdl/"
|
|
240
247
|
namespaces["xs"] = "http://www.w3.org/2001/XMLSchema"
|
|
@@ -310,7 +317,8 @@ def get_method_documentation(service_obj, method_name: str) -> Optional[Dict[str
|
|
|
310
317
|
doc_text = colorize("No description available.", "reset")
|
|
311
318
|
|
|
312
319
|
# 2. Get parameters from Python method signature using inspect
|
|
313
|
-
|
|
320
|
+
# Use object.__getattribute__ to bypass ONVIFService wrapper and get original method
|
|
321
|
+
method = object.__getattribute__(service_obj, method_name)
|
|
314
322
|
sig = inspect.signature(method)
|
|
315
323
|
for param in sig.parameters.values():
|
|
316
324
|
if param.name != "self":
|
|
@@ -321,10 +329,11 @@ def get_method_documentation(service_obj, method_name: str) -> Optional[Dict[str
|
|
|
321
329
|
|
|
322
330
|
return {"doc": doc_text, "required": required_args, "optional": optional_args}
|
|
323
331
|
|
|
324
|
-
except (
|
|
332
|
+
except (etree.ParseError, FileNotFoundError, AttributeError, ValueError):
|
|
325
333
|
# Fallback in case of any error, still try to get params
|
|
326
334
|
try:
|
|
327
|
-
|
|
335
|
+
# Use object.__getattribute__ to bypass ONVIFService wrapper and get original method
|
|
336
|
+
method = object.__getattribute__(service_obj, method_name)
|
|
328
337
|
sig = inspect.signature(method)
|
|
329
338
|
for param in sig.parameters.values():
|
|
330
339
|
if param.name != "self":
|
|
@@ -709,14 +718,19 @@ def get_operation_type_info(
|
|
|
709
718
|
Returns a dictionary with 'input' and 'output' keys containing message details.
|
|
710
719
|
"""
|
|
711
720
|
try:
|
|
712
|
-
import os
|
|
713
|
-
|
|
714
721
|
wsdl_path = service_obj.operator.wsdl_path
|
|
715
|
-
|
|
722
|
+
|
|
723
|
+
# Use secure lxml parser
|
|
724
|
+
parser = etree.XMLParser(
|
|
725
|
+
resolve_entities=False,
|
|
726
|
+
no_network=True,
|
|
727
|
+
remove_blank_text=True,
|
|
728
|
+
)
|
|
729
|
+
tree = etree.parse(wsdl_path, parser)
|
|
716
730
|
root = tree.getroot()
|
|
717
731
|
|
|
718
732
|
namespaces = {
|
|
719
|
-
node[0]: node[1] for node in
|
|
733
|
+
node[0]: node[1] for node in etree.iterparse(wsdl_path, events=["start-ns"])
|
|
720
734
|
}
|
|
721
735
|
namespaces["wsdl"] = "http://schemas.xmlsoap.org/wsdl/"
|
|
722
736
|
namespaces["xs"] = "http://www.w3.org/2001/XMLSchema"
|
|
@@ -773,7 +787,6 @@ def _load_imported_schemas(root, schema_context: dict, namespaces: dict):
|
|
|
773
787
|
"""
|
|
774
788
|
Recursively load all imported and included schemas into the schema context.
|
|
775
789
|
"""
|
|
776
|
-
import os
|
|
777
790
|
|
|
778
791
|
# Find all xs:import and xs:include in xs:schema elements
|
|
779
792
|
for schema in root.findall(".//xs:schema", namespaces):
|
|
@@ -787,7 +800,13 @@ def _load_imported_schemas(root, schema_context: dict, namespaces: dict):
|
|
|
787
800
|
|
|
788
801
|
if os.path.exists(schema_path):
|
|
789
802
|
try:
|
|
790
|
-
|
|
803
|
+
# Use secure parser for imported schemas
|
|
804
|
+
parser = etree.XMLParser(
|
|
805
|
+
resolve_entities=False,
|
|
806
|
+
no_network=True,
|
|
807
|
+
remove_blank_text=True,
|
|
808
|
+
)
|
|
809
|
+
imported_tree = etree.parse(schema_path, parser)
|
|
791
810
|
imported_root = imported_tree.getroot()
|
|
792
811
|
|
|
793
812
|
# Add to context if not already present
|
|
@@ -817,7 +836,13 @@ def _load_imported_schemas(root, schema_context: dict, namespaces: dict):
|
|
|
817
836
|
|
|
818
837
|
if os.path.exists(schema_path):
|
|
819
838
|
try:
|
|
820
|
-
|
|
839
|
+
# Use secure parser for included schemas
|
|
840
|
+
parser = etree.XMLParser(
|
|
841
|
+
resolve_entities=False,
|
|
842
|
+
no_network=True,
|
|
843
|
+
remove_blank_text=True,
|
|
844
|
+
)
|
|
845
|
+
included_tree = etree.parse(schema_path, parser)
|
|
821
846
|
included_root = included_tree.getroot()
|
|
822
847
|
|
|
823
848
|
# Add to context if not already present
|