onvif-python 0.0.9__tar.gz → 0.1.0__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.0.9/onvif_python.egg-info → onvif_python-0.1.0}/PKG-INFO +46 -9
- {onvif_python-0.0.9 → onvif_python-0.1.0}/README.md +45 -8
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/client.py +54 -36
- onvif_python-0.1.0/onvif/utils/wsdl.py +350 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0/onvif_python.egg-info}/PKG-INFO +46 -9
- {onvif_python-0.0.9 → onvif_python-0.1.0}/pyproject.toml +1 -1
- onvif_python-0.0.9/onvif/utils/wsdl.py +0 -297
- {onvif_python-0.0.9 → onvif_python-0.1.0}/LICENSE.md +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/MANIFEST.in +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/__init__.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/operator.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/__init__.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/accesscontrol.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/accessrules.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/actionengine.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/analytics/analytics.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/analytics/ruleengine.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/analyticsdevice.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/appmgmt.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/authenticationbehavior.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/credential.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/deviceio.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/devicemgmt.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/display.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/doorcontrol.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/events/events.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/events/notification.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/events/pullpoint.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/events/subscription.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/imaging.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/media.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/media2.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/provisioning.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/ptz.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/receiver.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/recording.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/replay.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/schedule.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/search.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/security/advancedsecurity.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/security/authorizationserver.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/security/dot1x.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/security/jwt.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/security/keystore.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/security/mediasigning.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/security/tlsserver.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/thermal.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/services/uplink.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/utils/__init__.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/utils/error_handlers.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/utils/exceptions.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/utils/xml_capture.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/utils/zeep.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/display/display.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/display.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/receiver.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/recording.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/replay.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/schema/common.xsd +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/search.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/index.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif_python.egg-info/SOURCES.txt +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif_python.egg-info/dependency_links.txt +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif_python.egg-info/requires.txt +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/onvif_python.egg-info/top_level.txt +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/setup.cfg +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/setup.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/tests/test_client.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/tests/test_core.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/tests/test_exceptions.py +0 -0
- {onvif_python-0.0.9 → onvif_python-0.1.0}/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.0
|
|
3
|
+
Version: 0.1.0
|
|
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
|
|
@@ -40,9 +40,9 @@ Dynamic: license-file
|
|
|
40
40
|
|
|
41
41
|
[](https://github.com/nirsimetri/onvif-python?tab=MIT-1-ov-file)
|
|
42
42
|
[](https://deepwiki.com/nirsimetri/onvif-python)
|
|
43
|
-
[](https://github.com/nirsimetri/onvif-python/releases)
|
|
44
44
|
<br>
|
|
45
|
-
[](https://pypi.org/project/onvif-python/)
|
|
46
46
|
[](https://clickpy.clickhouse.com/dashboard/onvif-python)
|
|
47
47
|
|
|
48
48
|
**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.
|
|
@@ -93,7 +93,14 @@ Create an instance of `ONVIFClient` by providing your device's IP address, port,
|
|
|
93
93
|
```python
|
|
94
94
|
from onvif import ONVIFClient
|
|
95
95
|
|
|
96
|
+
# Basic connection
|
|
96
97
|
client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
|
|
98
|
+
|
|
99
|
+
# With custom WSDL directory (optional)
|
|
100
|
+
client = ONVIFClient(
|
|
101
|
+
"192.168.1.17", 8000, "admin", "admin123",
|
|
102
|
+
wsdl_dir="/path/to/custom/wsdl" # Use custom WSDL files in this path
|
|
103
|
+
)
|
|
97
104
|
```
|
|
98
105
|
|
|
99
106
|
**2. Create Service Instance**
|
|
@@ -186,8 +193,9 @@ The `ONVIFClient` class provides various configuration options to customize the
|
|
|
186
193
|
|
|
187
194
|
| Parameter | Type | Required | Default | Description |
|
|
188
195
|
|-----------|------|----------|---------|-------------|
|
|
189
|
-
| `apply_patch` | `bool` | ❌ No | `True` | Enable zeep patching for better `xsd:any` field parsing and automatic flattening |
|
|
190
|
-
| `capture_xml` | `bool` | ❌ No | `False` | Enable XML capture plugin for debugging SOAP requests/responses |
|
|
196
|
+
| `apply_patch` | `bool` | ❌ No | `True` | Enable zeep patching for better `xsd:any` field parsing and automatic flattening, applied at (>= [`v0.0.4`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.0.4)) |
|
|
197
|
+
| `capture_xml` | `bool` | ❌ No | `False` | Enable XML capture plugin for debugging SOAP requests/responses, applied at (>= [`v0.0.6`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.0.6)) |
|
|
198
|
+
| `wsdl_dir` | `str` | ❌ No | `None` | Custom WSDL directory path for using external WSDL files instead of built-in ones (e.g., `/path/to/custom/wsdl`), applied at (>= [`v0.1.0`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.1.0)) |
|
|
191
199
|
|
|
192
200
|
</details>
|
|
193
201
|
|
|
@@ -317,6 +325,33 @@ if client.xml_plugin:
|
|
|
317
325
|
> - `save_to_file(request_file, response_file)` - Save XML to files
|
|
318
326
|
> - `clear_history()` - Clear captured history
|
|
319
327
|
|
|
328
|
+
**Custom WSDL Directory:**
|
|
329
|
+
```python
|
|
330
|
+
from onvif import ONVIFClient
|
|
331
|
+
|
|
332
|
+
# Use custom WSDL files instead of built-in ones
|
|
333
|
+
client = ONVIFClient(
|
|
334
|
+
"192.168.1.17",
|
|
335
|
+
80,
|
|
336
|
+
"admin",
|
|
337
|
+
"password",
|
|
338
|
+
wsdl_dir="/path/to/custom/wsdl" # Custom WSDL directory
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
# All services will automatically use custom WSDL files
|
|
342
|
+
device = client.devicemgmt()
|
|
343
|
+
media = client.media()
|
|
344
|
+
ptz = client.ptz()
|
|
345
|
+
|
|
346
|
+
# The custom WSDL directory should have a flat structure:
|
|
347
|
+
# /path/to/custom/wsdl/
|
|
348
|
+
# ├── devicemgmt.wsdl
|
|
349
|
+
# ├── media.wsdl
|
|
350
|
+
# ├── ptz.wsdl
|
|
351
|
+
# ├── imaging.wsdl
|
|
352
|
+
# └── ... (other WSDL files)
|
|
353
|
+
```
|
|
354
|
+
|
|
320
355
|
</details>
|
|
321
356
|
|
|
322
357
|
<details>
|
|
@@ -336,7 +371,8 @@ client = ONVIFClient(
|
|
|
336
371
|
use_https=True, # Secure communication
|
|
337
372
|
verify_ssl=True, # Verify certificates (default)
|
|
338
373
|
apply_patch=True, # Enhanced parsing (default)
|
|
339
|
-
capture_xml=False
|
|
374
|
+
capture_xml=False, # Disable debug mode (default)
|
|
375
|
+
wsdl_dir=None # Use built-in WSDL files (default)
|
|
340
376
|
)
|
|
341
377
|
```
|
|
342
378
|
</details>
|
|
@@ -346,6 +382,7 @@ client = ONVIFClient(
|
|
|
346
382
|
- **Authentication:** This library uses **WS-UsernameToken with Digest** authentication by default, which is the standard for ONVIF devices.
|
|
347
383
|
- **Patching:** The `apply_patch=True` (default) enables custom zeep patching that improves `xsd:any` field parsing. This is recommended for better compatibility with ONVIF responses.
|
|
348
384
|
- **XML Capture:** Only use `capture_xml=True` during development/debugging as it increases memory usage and may expose sensitive data in logs.
|
|
385
|
+
- **Custom WSDL:** Use `wsdl_dir` parameter to specify a custom directory containing WSDL files. The directory should have a flat structure with WSDL files directly in the root (e.g., `/path/to/custom/wsdl/devicemgmt.wsdl`, `/path/to/custom/wsdl/media.wsdl`, etc.).
|
|
349
386
|
- **Cache Location:** Disk cache (when using `CacheMode.DB` or `CacheMode.ALL`) is stored in `~/.onvif-python/onvif_zeep_cache.sqlite`.
|
|
350
387
|
|
|
351
388
|
## Service Discovery: Understanding Device Capabilities
|
|
@@ -592,10 +629,10 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
|
|
|
592
629
|
|
|
593
630
|
## Future Improvements (Stay tuned and star ⭐ this repo)
|
|
594
631
|
|
|
595
|
-
- [x] Add debugging mode with raw xml on SOAP requests and responses
|
|
596
|
-
- [
|
|
597
|
-
- [ ] Add asynchronous (async/await) support for non-blocking ONVIF operations and concurrent device communication.
|
|
632
|
+
- [x] ~~Add debugging mode with raw xml on SOAP requests and responses.~~ ([c258162](https://github.com/nirsimetri/onvif-python/commit/c258162))
|
|
633
|
+
- [x] ~~Add functionality for `ONVIFClient` to accept a custom `wsdl_dir` service.~~ ([65f2570](https://github.com/nirsimetri/onvif-python/commit/65f257092e4c9daa23dd0d00825ed38a45d23b70))
|
|
598
634
|
- [ ] Add `ONVIF CLI` program to interact directly with ONVIF devices via terminal.
|
|
635
|
+
- [ ] Add asynchronous (async/await) support for non-blocking ONVIF operations and concurrent device communication.
|
|
599
636
|
- [ ] Implement structured data models for ONVIF Schemas using [xsdata](https://github.com/tefra/xsdata).
|
|
600
637
|
- [ ] Integrate [xmltodict](https://github.com/martinblech/xmltodict) for simplified XML parsing and conversion.
|
|
601
638
|
- [ ] Enhance documentation with API references and diagrams (not from [AI Wiki](https://deepwiki.com/nirsimetri/onvif-python)).
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/nirsimetri/onvif-python?tab=MIT-1-ov-file)
|
|
4
4
|
[](https://deepwiki.com/nirsimetri/onvif-python)
|
|
5
|
-
[](https://github.com/nirsimetri/onvif-python/releases)
|
|
6
6
|
<br>
|
|
7
|
-
[](https://pypi.org/project/onvif-python/)
|
|
8
8
|
[](https://clickpy.clickhouse.com/dashboard/onvif-python)
|
|
9
9
|
|
|
10
10
|
**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.
|
|
@@ -55,7 +55,14 @@ Create an instance of `ONVIFClient` by providing your device's IP address, port,
|
|
|
55
55
|
```python
|
|
56
56
|
from onvif import ONVIFClient
|
|
57
57
|
|
|
58
|
+
# Basic connection
|
|
58
59
|
client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
|
|
60
|
+
|
|
61
|
+
# With custom WSDL directory (optional)
|
|
62
|
+
client = ONVIFClient(
|
|
63
|
+
"192.168.1.17", 8000, "admin", "admin123",
|
|
64
|
+
wsdl_dir="/path/to/custom/wsdl" # Use custom WSDL files in this path
|
|
65
|
+
)
|
|
59
66
|
```
|
|
60
67
|
|
|
61
68
|
**2. Create Service Instance**
|
|
@@ -148,8 +155,9 @@ The `ONVIFClient` class provides various configuration options to customize the
|
|
|
148
155
|
|
|
149
156
|
| Parameter | Type | Required | Default | Description |
|
|
150
157
|
|-----------|------|----------|---------|-------------|
|
|
151
|
-
| `apply_patch` | `bool` | ❌ No | `True` | Enable zeep patching for better `xsd:any` field parsing and automatic flattening |
|
|
152
|
-
| `capture_xml` | `bool` | ❌ No | `False` | Enable XML capture plugin for debugging SOAP requests/responses |
|
|
158
|
+
| `apply_patch` | `bool` | ❌ No | `True` | Enable zeep patching for better `xsd:any` field parsing and automatic flattening, applied at (>= [`v0.0.4`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.0.4)) |
|
|
159
|
+
| `capture_xml` | `bool` | ❌ No | `False` | Enable XML capture plugin for debugging SOAP requests/responses, applied at (>= [`v0.0.6`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.0.6)) |
|
|
160
|
+
| `wsdl_dir` | `str` | ❌ No | `None` | Custom WSDL directory path for using external WSDL files instead of built-in ones (e.g., `/path/to/custom/wsdl`), applied at (>= [`v0.1.0`](https://github.com/nirsimetri/onvif-python/releases/tag/v0.1.0)) |
|
|
153
161
|
|
|
154
162
|
</details>
|
|
155
163
|
|
|
@@ -279,6 +287,33 @@ if client.xml_plugin:
|
|
|
279
287
|
> - `save_to_file(request_file, response_file)` - Save XML to files
|
|
280
288
|
> - `clear_history()` - Clear captured history
|
|
281
289
|
|
|
290
|
+
**Custom WSDL Directory:**
|
|
291
|
+
```python
|
|
292
|
+
from onvif import ONVIFClient
|
|
293
|
+
|
|
294
|
+
# Use custom WSDL files instead of built-in ones
|
|
295
|
+
client = ONVIFClient(
|
|
296
|
+
"192.168.1.17",
|
|
297
|
+
80,
|
|
298
|
+
"admin",
|
|
299
|
+
"password",
|
|
300
|
+
wsdl_dir="/path/to/custom/wsdl" # Custom WSDL directory
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# All services will automatically use custom WSDL files
|
|
304
|
+
device = client.devicemgmt()
|
|
305
|
+
media = client.media()
|
|
306
|
+
ptz = client.ptz()
|
|
307
|
+
|
|
308
|
+
# The custom WSDL directory should have a flat structure:
|
|
309
|
+
# /path/to/custom/wsdl/
|
|
310
|
+
# ├── devicemgmt.wsdl
|
|
311
|
+
# ├── media.wsdl
|
|
312
|
+
# ├── ptz.wsdl
|
|
313
|
+
# ├── imaging.wsdl
|
|
314
|
+
# └── ... (other WSDL files)
|
|
315
|
+
```
|
|
316
|
+
|
|
282
317
|
</details>
|
|
283
318
|
|
|
284
319
|
<details>
|
|
@@ -298,7 +333,8 @@ client = ONVIFClient(
|
|
|
298
333
|
use_https=True, # Secure communication
|
|
299
334
|
verify_ssl=True, # Verify certificates (default)
|
|
300
335
|
apply_patch=True, # Enhanced parsing (default)
|
|
301
|
-
capture_xml=False
|
|
336
|
+
capture_xml=False, # Disable debug mode (default)
|
|
337
|
+
wsdl_dir=None # Use built-in WSDL files (default)
|
|
302
338
|
)
|
|
303
339
|
```
|
|
304
340
|
</details>
|
|
@@ -308,6 +344,7 @@ client = ONVIFClient(
|
|
|
308
344
|
- **Authentication:** This library uses **WS-UsernameToken with Digest** authentication by default, which is the standard for ONVIF devices.
|
|
309
345
|
- **Patching:** The `apply_patch=True` (default) enables custom zeep patching that improves `xsd:any` field parsing. This is recommended for better compatibility with ONVIF responses.
|
|
310
346
|
- **XML Capture:** Only use `capture_xml=True` during development/debugging as it increases memory usage and may expose sensitive data in logs.
|
|
347
|
+
- **Custom WSDL:** Use `wsdl_dir` parameter to specify a custom directory containing WSDL files. The directory should have a flat structure with WSDL files directly in the root (e.g., `/path/to/custom/wsdl/devicemgmt.wsdl`, `/path/to/custom/wsdl/media.wsdl`, etc.).
|
|
311
348
|
- **Cache Location:** Disk cache (when using `CacheMode.DB` or `CacheMode.ALL`) is stored in `~/.onvif-python/onvif_zeep_cache.sqlite`.
|
|
312
349
|
|
|
313
350
|
## Service Discovery: Understanding Device Capabilities
|
|
@@ -554,10 +591,10 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
|
|
|
554
591
|
|
|
555
592
|
## Future Improvements (Stay tuned and star ⭐ this repo)
|
|
556
593
|
|
|
557
|
-
- [x] Add debugging mode with raw xml on SOAP requests and responses
|
|
558
|
-
- [
|
|
559
|
-
- [ ] Add asynchronous (async/await) support for non-blocking ONVIF operations and concurrent device communication.
|
|
594
|
+
- [x] ~~Add debugging mode with raw xml on SOAP requests and responses.~~ ([c258162](https://github.com/nirsimetri/onvif-python/commit/c258162))
|
|
595
|
+
- [x] ~~Add functionality for `ONVIFClient` to accept a custom `wsdl_dir` service.~~ ([65f2570](https://github.com/nirsimetri/onvif-python/commit/65f257092e4c9daa23dd0d00825ed38a45d23b70))
|
|
560
596
|
- [ ] Add `ONVIF CLI` program to interact directly with ONVIF devices via terminal.
|
|
597
|
+
- [ ] Add asynchronous (async/await) support for non-blocking ONVIF operations and concurrent device communication.
|
|
561
598
|
- [ ] Implement structured data models for ONVIF Schemas using [xsdata](https://github.com/tefra/xsdata).
|
|
562
599
|
- [ ] Integrate [xmltodict](https://github.com/martinblech/xmltodict) for simplified XML parsing and conversion.
|
|
563
600
|
- [ ] Enhance documentation with API references and diagrams (not from [AI Wiki](https://deepwiki.com/nirsimetri/onvif-python)).
|
|
@@ -57,6 +57,7 @@ class ONVIFClient:
|
|
|
57
57
|
verify_ssl=True,
|
|
58
58
|
apply_patch=True,
|
|
59
59
|
capture_xml=False,
|
|
60
|
+
wsdl_dir=None,
|
|
60
61
|
):
|
|
61
62
|
# Apply or remove zeep patch based on user preference
|
|
62
63
|
if apply_patch:
|
|
@@ -69,6 +70,11 @@ class ONVIFClient:
|
|
|
69
70
|
if capture_xml:
|
|
70
71
|
self.xml_plugin = XMLCapturePlugin()
|
|
71
72
|
|
|
73
|
+
# Store custom WSDL directory if provided
|
|
74
|
+
self.wsdl_dir = wsdl_dir
|
|
75
|
+
if wsdl_dir:
|
|
76
|
+
ONVIFWSDL.set_custom_wsdl_dir(wsdl_dir)
|
|
77
|
+
|
|
72
78
|
# Pass to ONVIFOperator
|
|
73
79
|
self.common_args = {
|
|
74
80
|
"host": host,
|
|
@@ -114,9 +120,9 @@ class ONVIFClient:
|
|
|
114
120
|
# Lazy init for other services
|
|
115
121
|
|
|
116
122
|
self._events = None
|
|
117
|
-
self.
|
|
123
|
+
self._pullpoints = {} # Dictionary for multiple PullPoint instances
|
|
118
124
|
self._notification = None
|
|
119
|
-
self.
|
|
125
|
+
self._subscriptions = {} # Dictionary for multiple Subscription instances
|
|
120
126
|
|
|
121
127
|
self._imaging = None
|
|
122
128
|
|
|
@@ -286,24 +292,27 @@ class ONVIFClient:
|
|
|
286
292
|
return self._events
|
|
287
293
|
|
|
288
294
|
def pullpoint(self, SubscriptionRef):
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if not xaddr:
|
|
301
|
-
raise RuntimeError(
|
|
302
|
-
"SubscriptionReference.Address missing in subscription response"
|
|
303
|
-
)
|
|
295
|
+
xaddr = None
|
|
296
|
+
try:
|
|
297
|
+
addr_obj = SubscriptionRef["SubscriptionReference"]["Address"]
|
|
298
|
+
if isinstance(addr_obj, dict) and "_value_1" in addr_obj:
|
|
299
|
+
xaddr = addr_obj["_value_1"]
|
|
300
|
+
elif hasattr(addr_obj, "_value_1"):
|
|
301
|
+
xaddr = addr_obj._value_1
|
|
302
|
+
|
|
303
|
+
xaddr = self._rewrite_xaddr_if_needed(xaddr)
|
|
304
|
+
except Exception:
|
|
305
|
+
pass
|
|
304
306
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
+
if not xaddr:
|
|
308
|
+
raise RuntimeError(
|
|
309
|
+
"SubscriptionReference.Address missing in subscription response"
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
if xaddr not in self._pullpoints:
|
|
313
|
+
self._pullpoints[xaddr] = PullPoint(xaddr=xaddr, **self.common_args)
|
|
314
|
+
|
|
315
|
+
return self._pullpoints[xaddr]
|
|
307
316
|
|
|
308
317
|
def notification(self):
|
|
309
318
|
if self._notification is None:
|
|
@@ -313,24 +322,27 @@ class ONVIFClient:
|
|
|
313
322
|
return self._notification
|
|
314
323
|
|
|
315
324
|
def subscription(self, SubscriptionRef):
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
if not xaddr:
|
|
328
|
-
raise RuntimeError(
|
|
329
|
-
"SubscriptionReference.Address missing in subscription response"
|
|
330
|
-
)
|
|
325
|
+
xaddr = None
|
|
326
|
+
try:
|
|
327
|
+
addr_obj = SubscriptionRef["SubscriptionReference"]["Address"]
|
|
328
|
+
if isinstance(addr_obj, dict) and "_value_1" in addr_obj:
|
|
329
|
+
xaddr = addr_obj["_value_1"]
|
|
330
|
+
elif hasattr(addr_obj, "_value_1"):
|
|
331
|
+
xaddr = addr_obj._value_1
|
|
332
|
+
|
|
333
|
+
xaddr = self._rewrite_xaddr_if_needed(xaddr)
|
|
334
|
+
except Exception:
|
|
335
|
+
pass
|
|
331
336
|
|
|
332
|
-
|
|
333
|
-
|
|
337
|
+
if not xaddr:
|
|
338
|
+
raise RuntimeError(
|
|
339
|
+
"SubscriptionReference.Address missing in subscription response"
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
if xaddr not in self._subscriptions:
|
|
343
|
+
self._subscriptions[xaddr] = Subscription(xaddr=xaddr, **self.common_args)
|
|
344
|
+
|
|
345
|
+
return self._subscriptions[xaddr]
|
|
334
346
|
|
|
335
347
|
# Imaging
|
|
336
348
|
|
|
@@ -566,26 +578,31 @@ class ONVIFClient:
|
|
|
566
578
|
|
|
567
579
|
def jwt(self, xaddr):
|
|
568
580
|
if self._jwt is None:
|
|
581
|
+
xaddr = self._rewrite_xaddr_if_needed(xaddr)
|
|
569
582
|
self._jwt = JWT(xaddr=xaddr, **self.common_args)
|
|
570
583
|
return self._jwt
|
|
571
584
|
|
|
572
585
|
def keystore(self, xaddr):
|
|
573
586
|
if self._keystore is None:
|
|
587
|
+
xaddr = self._rewrite_xaddr_if_needed(xaddr)
|
|
574
588
|
self._keystore = Keystore(xaddr=xaddr, **self.common_args)
|
|
575
589
|
return self._keystore
|
|
576
590
|
|
|
577
591
|
def tlsserver(self, xaddr):
|
|
578
592
|
if self._tlsserver is None:
|
|
593
|
+
xaddr = self._rewrite_xaddr_if_needed(xaddr)
|
|
579
594
|
self._tlsserver = TLSServer(xaddr=xaddr, **self.common_args)
|
|
580
595
|
return self._tlsserver
|
|
581
596
|
|
|
582
597
|
def dot1x(self, xaddr):
|
|
583
598
|
if self._dot1x is None:
|
|
599
|
+
xaddr = self._rewrite_xaddr_if_needed(xaddr)
|
|
584
600
|
self._dot1x = Dot1X(xaddr=xaddr, **self.common_args)
|
|
585
601
|
return self._dot1x
|
|
586
602
|
|
|
587
603
|
def authorizationserver(self, xaddr):
|
|
588
604
|
if self._authorizationserver is None:
|
|
605
|
+
xaddr = self._rewrite_xaddr_if_needed(xaddr)
|
|
589
606
|
self._authorizationserver = AuthorizationServer(
|
|
590
607
|
xaddr=xaddr, **self.common_args
|
|
591
608
|
)
|
|
@@ -593,5 +610,6 @@ class ONVIFClient:
|
|
|
593
610
|
|
|
594
611
|
def mediasigning(self, xaddr):
|
|
595
612
|
if self._mediasigning is None:
|
|
613
|
+
xaddr = self._rewrite_xaddr_if_needed(xaddr)
|
|
596
614
|
self._mediasigning = MediaSigning(xaddr=xaddr, **self.common_args)
|
|
597
615
|
return self._mediasigning
|