onvif-python 0.0.6__tar.gz → 0.0.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {onvif_python-0.0.6/onvif_python.egg-info → onvif_python-0.0.7}/PKG-INFO +188 -3
- {onvif_python-0.0.6 → onvif_python-0.0.7}/README.md +187 -2
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/__init__.py +3 -5
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/client.py +3 -8
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/operator.py +3 -4
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/imaging.py +3 -1
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/provisioning.py +1 -1
- onvif_python-0.0.7/onvif/utils/__init__.py +14 -0
- onvif_python-0.0.7/onvif/utils/error_handlers.py +76 -0
- onvif_python-0.0.7/onvif/utils/exceptions.py +69 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/utils/xml_capture.py +38 -38
- onvif_python-0.0.7/onvif/utils/zeep.py +385 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7/onvif_python.egg-info}/PKG-INFO +188 -3
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif_python.egg-info/SOURCES.txt +1 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/pyproject.toml +1 -1
- onvif_python-0.0.6/onvif/utils/__init__.py +0 -5
- onvif_python-0.0.6/onvif/utils/exceptions.py +0 -33
- onvif_python-0.0.6/onvif/utils/zeep.py +0 -286
- {onvif_python-0.0.6 → onvif_python-0.0.7}/LICENSE.md +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/MANIFEST.in +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/__init__.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/accesscontrol.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/accessrules.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/actionengine.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/analytics/analytics.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/analytics/ruleengine.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/analyticsdevice.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/appmgmt.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/authenticationbehavior.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/credential.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/deviceio.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/devicemgmt.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/display.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/doorcontrol.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/events/events.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/events/notification.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/events/pullpoint.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/events/subscription.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/media.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/media2.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/ptz.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/receiver.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/recording.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/replay.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/schedule.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/search.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/security/advancedsecurity.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/security/authorizationserver.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/security/dot1x.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/security/jwt.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/security/keystore.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/security/mediasigning.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/security/tlsserver.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/thermal.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/services/uplink.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/utils/wsdl.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/display/display.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/display.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/receiver.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/recording.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/replay.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/schema/common.xsd +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/search.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/analytics/index.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif_python.egg-info/dependency_links.txt +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif_python.egg-info/requires.txt +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/onvif_python.egg-info/top_level.txt +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/setup.cfg +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/setup.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/tests/test_client.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/tests/test_core.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/tests/test_exceptions.py +0 -0
- {onvif_python-0.0.6 → onvif_python-0.0.7}/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.0.7
|
|
4
4
|
Summary: A comprehensive and developer-friendly Python library for working with ONVIF-compliant devices
|
|
5
5
|
Author-email: Nirsimetri Technologies® <open@nirsimetri.com>
|
|
6
6
|
Maintainer: Kaburagi
|
|
@@ -43,9 +43,9 @@ Dynamic: license-file
|
|
|
43
43
|
|
|
44
44
|
[](https://github.com/nirsimetri/onvif-python?tab=MIT-1-ov-file)
|
|
45
45
|
[](https://deepwiki.com/nirsimetri/onvif-python)
|
|
46
|
-
[](https://github.com/nirsimetri/onvif-python/releases)
|
|
47
47
|
<br>
|
|
48
|
-
[](https://pypi.org/project/onvif-python/)
|
|
49
49
|
[](https://clickpy.clickhouse.com/dashboard/onvif-python)
|
|
50
50
|
|
|
51
51
|
Are you having trouble finding a Python ONVIF library that supports your device?
|
|
@@ -150,6 +150,191 @@ Explore more advanced usage and service-specific operations in the [`examples/`]
|
|
|
150
150
|
> [!IMPORTANT]
|
|
151
151
|
> If you're new to ONVIF and want to learn more, we highly recommend taking the official free online course provided by ONVIF at [Introduction to ONVIF Course](https://www.onvif.org/about/introduction-to-onvif-course). Please note that we are not endorsed or sponsored by ONVIF, see [Legal Notice](#legal-notice) for details.
|
|
152
152
|
|
|
153
|
+
## ONVIFClient Parameters
|
|
154
|
+
|
|
155
|
+
The `ONVIFClient` class provides various configuration options to customize the connection behavior, caching strategy, security settings, and debugging capabilities. Below is a detailed description of all available parameters:
|
|
156
|
+
|
|
157
|
+
### Basic Parameters
|
|
158
|
+
|
|
159
|
+
| Parameter | Type | Required | Default | Description |
|
|
160
|
+
|-----------|------|----------|---------|-------------|
|
|
161
|
+
| `host` | `str` | ✅ Yes | - | IP address or hostname of the ONVIF device (e.g., `"192.168.1.17"`) |
|
|
162
|
+
| `port` | `int` | ✅ Yes | - | Port number for ONVIF service (common ports: `80`, `8000`, `8080`) |
|
|
163
|
+
| `username` | `str` | ✅ Yes | - | Username for device authentication (use digest authentication) |
|
|
164
|
+
| `password` | `str` | ✅ Yes | - | Password for device authentication |
|
|
165
|
+
|
|
166
|
+
### Connection Parameters
|
|
167
|
+
|
|
168
|
+
| Parameter | Type | Required | Default | Description |
|
|
169
|
+
|-----------|------|----------|---------|-------------|
|
|
170
|
+
| `timeout` | `int` | ❌ No | `10` | Connection timeout in seconds for SOAP requests |
|
|
171
|
+
| `use_https` | `bool` | ❌ No | `False` | Use HTTPS instead of HTTP for secure communication |
|
|
172
|
+
| `verify_ssl` | `bool` | ❌ No | `True` | Verify SSL certificates when using HTTPS (set to `False` for self-signed certificates) |
|
|
173
|
+
|
|
174
|
+
### Caching Parameters
|
|
175
|
+
|
|
176
|
+
| Parameter | Type | Required | Default | Description |
|
|
177
|
+
|-----------|------|----------|---------|-------------|
|
|
178
|
+
| `cache` | `CacheMode` | ❌ No | `CacheMode.ALL` | WSDL caching strategy (see [Cache Modes](#cache-modes) below) |
|
|
179
|
+
|
|
180
|
+
### Feature Parameters
|
|
181
|
+
|
|
182
|
+
| Parameter | Type | Required | Default | Description |
|
|
183
|
+
|-----------|------|----------|---------|-------------|
|
|
184
|
+
| `apply_patch` | `bool` | ❌ No | `True` | Enable zeep patching for better xsd:any field parsing and automatic flattening |
|
|
185
|
+
| `capture_xml` | `bool` | ❌ No | `False` | Enable XML capture plugin for debugging SOAP requests/responses |
|
|
186
|
+
|
|
187
|
+
### Cache Modes
|
|
188
|
+
|
|
189
|
+
The library provides four caching strategies via the `CacheMode` enum:
|
|
190
|
+
|
|
191
|
+
| Mode | Description | Best For | Startup Speed | Disk Usage | Memory Usage |
|
|
192
|
+
|------|-------------|----------|---------------|------------|--------------|
|
|
193
|
+
| `CacheMode.ALL` | In-memory + disk cache (SQLite) | Production servers, multi-device apps | ⚡⚡⚡ Fast | 💾 High | 🧠 High |
|
|
194
|
+
| `CacheMode.DB` | Disk cache only (SQLite) | Batch jobs, CLI tools | ⚡⚡ Medium | 💾 Medium | 🧠 Low |
|
|
195
|
+
| `CacheMode.MEM` | In-memory cache only | Short-lived scripts, demos | ⚡⚡ Medium | 💾 None | 🧠 Medium |
|
|
196
|
+
| `CacheMode.NONE` | No caching | Testing, debugging | ⚡ Slow | 💾 None | 🧠 Low |
|
|
197
|
+
|
|
198
|
+
**Recommendation:** Use `CacheMode.ALL` (default) for production applications to maximize performance.
|
|
199
|
+
|
|
200
|
+
### Usage Examples
|
|
201
|
+
|
|
202
|
+
**Basic Connection:**
|
|
203
|
+
```python
|
|
204
|
+
from onvif import ONVIFClient
|
|
205
|
+
|
|
206
|
+
# Minimal configuration
|
|
207
|
+
client = ONVIFClient("192.168.1.17", 80, "admin", "password")
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Secure Connection (HTTPS):**
|
|
211
|
+
```python
|
|
212
|
+
from onvif import ONVIFClient
|
|
213
|
+
|
|
214
|
+
# Connect via HTTPS with custom timeout
|
|
215
|
+
client = ONVIFClient(
|
|
216
|
+
"your-cctv-node.viewplexus.com",
|
|
217
|
+
443, # HTTPS port
|
|
218
|
+
"admin",
|
|
219
|
+
"password",
|
|
220
|
+
timeout=30,
|
|
221
|
+
use_https=True,
|
|
222
|
+
verify_ssl=False # For self-signed certificates
|
|
223
|
+
)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Performance Optimized (Memory Cache):**
|
|
227
|
+
```python
|
|
228
|
+
from onvif import ONVIFClient, CacheMode
|
|
229
|
+
|
|
230
|
+
# Use memory-only cache for quick scripts
|
|
231
|
+
client = ONVIFClient(
|
|
232
|
+
"192.168.1.17",
|
|
233
|
+
80,
|
|
234
|
+
"admin",
|
|
235
|
+
"password",
|
|
236
|
+
cache=CacheMode.MEM
|
|
237
|
+
)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**No Caching (Testing):**
|
|
241
|
+
```python
|
|
242
|
+
from onvif import ONVIFClient, CacheMode
|
|
243
|
+
|
|
244
|
+
# Disable all caching for testing
|
|
245
|
+
client = ONVIFClient(
|
|
246
|
+
"192.168.1.17",
|
|
247
|
+
80,
|
|
248
|
+
"admin",
|
|
249
|
+
"password",
|
|
250
|
+
cache=CacheMode.NONE,
|
|
251
|
+
apply_patch=False # Use original zeep behavior
|
|
252
|
+
)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Debugging Mode (XML Capture):**
|
|
256
|
+
```python
|
|
257
|
+
from onvif import ONVIFClient
|
|
258
|
+
|
|
259
|
+
# Enable XML capture for debugging
|
|
260
|
+
client = ONVIFClient(
|
|
261
|
+
"192.168.1.17",
|
|
262
|
+
80,
|
|
263
|
+
"admin",
|
|
264
|
+
"password",
|
|
265
|
+
capture_xml=True # Captures all SOAP requests/responses
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# Make some ONVIF calls
|
|
269
|
+
device = client.devicemgmt()
|
|
270
|
+
info = device.GetDeviceInformation()
|
|
271
|
+
services = device.GetCapabilities()
|
|
272
|
+
|
|
273
|
+
# Access the XML capture plugin
|
|
274
|
+
if client.xml_plugin:
|
|
275
|
+
# Get last captured request/response
|
|
276
|
+
print("Last Request XML:")
|
|
277
|
+
print(client.xml_plugin.last_sent_xml)
|
|
278
|
+
|
|
279
|
+
print("\nLast Response XML:")
|
|
280
|
+
print(client.xml_plugin.last_received_xml)
|
|
281
|
+
|
|
282
|
+
print(f"\nLast Operation: {client.xml_plugin.last_operation}")
|
|
283
|
+
|
|
284
|
+
# Get complete history of all requests/responses
|
|
285
|
+
print(f"\nTotal captured operations: {len(client.xml_plugin.history)}")
|
|
286
|
+
for item in client.xml_plugin.history:
|
|
287
|
+
print(f" - {item['operation']} ({item['type']})")
|
|
288
|
+
|
|
289
|
+
# Save captured XML to files
|
|
290
|
+
client.xml_plugin.save_to_file(
|
|
291
|
+
request_file="last_request.xml",
|
|
292
|
+
response_file="last_response.xml"
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
# Clear history when done
|
|
296
|
+
client.xml_plugin.clear_history()
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
> [!NOTE]
|
|
300
|
+
> **XML Capture Plugin Methods:**
|
|
301
|
+
> - `last_sent_xml` - Get the last SOAP request XML
|
|
302
|
+
> - `last_received_xml` - Get the last SOAP response XML
|
|
303
|
+
> - `last_operation` - Get the name of the last operation
|
|
304
|
+
> - `history` - List of all captured requests/responses with metadata
|
|
305
|
+
> - `get_last_request()` - Method to get last request
|
|
306
|
+
> - `get_last_response()` - Method to get last response
|
|
307
|
+
> - `get_history()` - Method to get all history
|
|
308
|
+
> - `save_to_file(request_file, response_file)` - Save XML to files
|
|
309
|
+
> - `clear_history()` - Clear captured history
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
**Production Configuration:**
|
|
313
|
+
```python
|
|
314
|
+
from onvif import ONVIFClient, CacheMode
|
|
315
|
+
|
|
316
|
+
# Recommended production settings
|
|
317
|
+
client = ONVIFClient(
|
|
318
|
+
host="your-cctv-node.viewplexus.com",
|
|
319
|
+
port=443,
|
|
320
|
+
username="admin",
|
|
321
|
+
password="secure_password",
|
|
322
|
+
timeout=15,
|
|
323
|
+
cache=CacheMode.ALL, # Maximum performance
|
|
324
|
+
use_https=True, # Secure communication
|
|
325
|
+
verify_ssl=True, # Verify certificates (default)
|
|
326
|
+
apply_patch=True, # Enhanced parsing (default)
|
|
327
|
+
capture_xml=False # Disable debug mode (default)
|
|
328
|
+
)
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Notes
|
|
332
|
+
|
|
333
|
+
- **Authentication:** This library uses **WS-UsernameToken with Digest** authentication by default, which is the standard for ONVIF devices.
|
|
334
|
+
- **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.
|
|
335
|
+
- **XML Capture:** Only use `capture_xml=True` during development/debugging as it increases memory usage and may expose sensitive data in logs.
|
|
336
|
+
- **Cache Location:** Disk cache (when using `CacheMode.DB` or `CacheMode.ALL`) is stored in `~/.onvif-python/onvif_zeep_cache.sqlite`.
|
|
337
|
+
|
|
153
338
|
## Service Discovery: Understanding Device Capabilities
|
|
154
339
|
|
|
155
340
|
> [!WARNING]
|
|
@@ -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
|
Are you having trouble finding a Python ONVIF library that supports your device?
|
|
@@ -109,6 +109,191 @@ Explore more advanced usage and service-specific operations in the [`examples/`]
|
|
|
109
109
|
> [!IMPORTANT]
|
|
110
110
|
> If you're new to ONVIF and want to learn more, we highly recommend taking the official free online course provided by ONVIF at [Introduction to ONVIF Course](https://www.onvif.org/about/introduction-to-onvif-course). Please note that we are not endorsed or sponsored by ONVIF, see [Legal Notice](#legal-notice) for details.
|
|
111
111
|
|
|
112
|
+
## ONVIFClient Parameters
|
|
113
|
+
|
|
114
|
+
The `ONVIFClient` class provides various configuration options to customize the connection behavior, caching strategy, security settings, and debugging capabilities. Below is a detailed description of all available parameters:
|
|
115
|
+
|
|
116
|
+
### Basic Parameters
|
|
117
|
+
|
|
118
|
+
| Parameter | Type | Required | Default | Description |
|
|
119
|
+
|-----------|------|----------|---------|-------------|
|
|
120
|
+
| `host` | `str` | ✅ Yes | - | IP address or hostname of the ONVIF device (e.g., `"192.168.1.17"`) |
|
|
121
|
+
| `port` | `int` | ✅ Yes | - | Port number for ONVIF service (common ports: `80`, `8000`, `8080`) |
|
|
122
|
+
| `username` | `str` | ✅ Yes | - | Username for device authentication (use digest authentication) |
|
|
123
|
+
| `password` | `str` | ✅ Yes | - | Password for device authentication |
|
|
124
|
+
|
|
125
|
+
### Connection Parameters
|
|
126
|
+
|
|
127
|
+
| Parameter | Type | Required | Default | Description |
|
|
128
|
+
|-----------|------|----------|---------|-------------|
|
|
129
|
+
| `timeout` | `int` | ❌ No | `10` | Connection timeout in seconds for SOAP requests |
|
|
130
|
+
| `use_https` | `bool` | ❌ No | `False` | Use HTTPS instead of HTTP for secure communication |
|
|
131
|
+
| `verify_ssl` | `bool` | ❌ No | `True` | Verify SSL certificates when using HTTPS (set to `False` for self-signed certificates) |
|
|
132
|
+
|
|
133
|
+
### Caching Parameters
|
|
134
|
+
|
|
135
|
+
| Parameter | Type | Required | Default | Description |
|
|
136
|
+
|-----------|------|----------|---------|-------------|
|
|
137
|
+
| `cache` | `CacheMode` | ❌ No | `CacheMode.ALL` | WSDL caching strategy (see [Cache Modes](#cache-modes) below) |
|
|
138
|
+
|
|
139
|
+
### Feature Parameters
|
|
140
|
+
|
|
141
|
+
| Parameter | Type | Required | Default | Description |
|
|
142
|
+
|-----------|------|----------|---------|-------------|
|
|
143
|
+
| `apply_patch` | `bool` | ❌ No | `True` | Enable zeep patching for better xsd:any field parsing and automatic flattening |
|
|
144
|
+
| `capture_xml` | `bool` | ❌ No | `False` | Enable XML capture plugin for debugging SOAP requests/responses |
|
|
145
|
+
|
|
146
|
+
### Cache Modes
|
|
147
|
+
|
|
148
|
+
The library provides four caching strategies via the `CacheMode` enum:
|
|
149
|
+
|
|
150
|
+
| Mode | Description | Best For | Startup Speed | Disk Usage | Memory Usage |
|
|
151
|
+
|------|-------------|----------|---------------|------------|--------------|
|
|
152
|
+
| `CacheMode.ALL` | In-memory + disk cache (SQLite) | Production servers, multi-device apps | ⚡⚡⚡ Fast | 💾 High | 🧠 High |
|
|
153
|
+
| `CacheMode.DB` | Disk cache only (SQLite) | Batch jobs, CLI tools | ⚡⚡ Medium | 💾 Medium | 🧠 Low |
|
|
154
|
+
| `CacheMode.MEM` | In-memory cache only | Short-lived scripts, demos | ⚡⚡ Medium | 💾 None | 🧠 Medium |
|
|
155
|
+
| `CacheMode.NONE` | No caching | Testing, debugging | ⚡ Slow | 💾 None | 🧠 Low |
|
|
156
|
+
|
|
157
|
+
**Recommendation:** Use `CacheMode.ALL` (default) for production applications to maximize performance.
|
|
158
|
+
|
|
159
|
+
### Usage Examples
|
|
160
|
+
|
|
161
|
+
**Basic Connection:**
|
|
162
|
+
```python
|
|
163
|
+
from onvif import ONVIFClient
|
|
164
|
+
|
|
165
|
+
# Minimal configuration
|
|
166
|
+
client = ONVIFClient("192.168.1.17", 80, "admin", "password")
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Secure Connection (HTTPS):**
|
|
170
|
+
```python
|
|
171
|
+
from onvif import ONVIFClient
|
|
172
|
+
|
|
173
|
+
# Connect via HTTPS with custom timeout
|
|
174
|
+
client = ONVIFClient(
|
|
175
|
+
"your-cctv-node.viewplexus.com",
|
|
176
|
+
443, # HTTPS port
|
|
177
|
+
"admin",
|
|
178
|
+
"password",
|
|
179
|
+
timeout=30,
|
|
180
|
+
use_https=True,
|
|
181
|
+
verify_ssl=False # For self-signed certificates
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Performance Optimized (Memory Cache):**
|
|
186
|
+
```python
|
|
187
|
+
from onvif import ONVIFClient, CacheMode
|
|
188
|
+
|
|
189
|
+
# Use memory-only cache for quick scripts
|
|
190
|
+
client = ONVIFClient(
|
|
191
|
+
"192.168.1.17",
|
|
192
|
+
80,
|
|
193
|
+
"admin",
|
|
194
|
+
"password",
|
|
195
|
+
cache=CacheMode.MEM
|
|
196
|
+
)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**No Caching (Testing):**
|
|
200
|
+
```python
|
|
201
|
+
from onvif import ONVIFClient, CacheMode
|
|
202
|
+
|
|
203
|
+
# Disable all caching for testing
|
|
204
|
+
client = ONVIFClient(
|
|
205
|
+
"192.168.1.17",
|
|
206
|
+
80,
|
|
207
|
+
"admin",
|
|
208
|
+
"password",
|
|
209
|
+
cache=CacheMode.NONE,
|
|
210
|
+
apply_patch=False # Use original zeep behavior
|
|
211
|
+
)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Debugging Mode (XML Capture):**
|
|
215
|
+
```python
|
|
216
|
+
from onvif import ONVIFClient
|
|
217
|
+
|
|
218
|
+
# Enable XML capture for debugging
|
|
219
|
+
client = ONVIFClient(
|
|
220
|
+
"192.168.1.17",
|
|
221
|
+
80,
|
|
222
|
+
"admin",
|
|
223
|
+
"password",
|
|
224
|
+
capture_xml=True # Captures all SOAP requests/responses
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Make some ONVIF calls
|
|
228
|
+
device = client.devicemgmt()
|
|
229
|
+
info = device.GetDeviceInformation()
|
|
230
|
+
services = device.GetCapabilities()
|
|
231
|
+
|
|
232
|
+
# Access the XML capture plugin
|
|
233
|
+
if client.xml_plugin:
|
|
234
|
+
# Get last captured request/response
|
|
235
|
+
print("Last Request XML:")
|
|
236
|
+
print(client.xml_plugin.last_sent_xml)
|
|
237
|
+
|
|
238
|
+
print("\nLast Response XML:")
|
|
239
|
+
print(client.xml_plugin.last_received_xml)
|
|
240
|
+
|
|
241
|
+
print(f"\nLast Operation: {client.xml_plugin.last_operation}")
|
|
242
|
+
|
|
243
|
+
# Get complete history of all requests/responses
|
|
244
|
+
print(f"\nTotal captured operations: {len(client.xml_plugin.history)}")
|
|
245
|
+
for item in client.xml_plugin.history:
|
|
246
|
+
print(f" - {item['operation']} ({item['type']})")
|
|
247
|
+
|
|
248
|
+
# Save captured XML to files
|
|
249
|
+
client.xml_plugin.save_to_file(
|
|
250
|
+
request_file="last_request.xml",
|
|
251
|
+
response_file="last_response.xml"
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# Clear history when done
|
|
255
|
+
client.xml_plugin.clear_history()
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
> [!NOTE]
|
|
259
|
+
> **XML Capture Plugin Methods:**
|
|
260
|
+
> - `last_sent_xml` - Get the last SOAP request XML
|
|
261
|
+
> - `last_received_xml` - Get the last SOAP response XML
|
|
262
|
+
> - `last_operation` - Get the name of the last operation
|
|
263
|
+
> - `history` - List of all captured requests/responses with metadata
|
|
264
|
+
> - `get_last_request()` - Method to get last request
|
|
265
|
+
> - `get_last_response()` - Method to get last response
|
|
266
|
+
> - `get_history()` - Method to get all history
|
|
267
|
+
> - `save_to_file(request_file, response_file)` - Save XML to files
|
|
268
|
+
> - `clear_history()` - Clear captured history
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
**Production Configuration:**
|
|
272
|
+
```python
|
|
273
|
+
from onvif import ONVIFClient, CacheMode
|
|
274
|
+
|
|
275
|
+
# Recommended production settings
|
|
276
|
+
client = ONVIFClient(
|
|
277
|
+
host="your-cctv-node.viewplexus.com",
|
|
278
|
+
port=443,
|
|
279
|
+
username="admin",
|
|
280
|
+
password="secure_password",
|
|
281
|
+
timeout=15,
|
|
282
|
+
cache=CacheMode.ALL, # Maximum performance
|
|
283
|
+
use_https=True, # Secure communication
|
|
284
|
+
verify_ssl=True, # Verify certificates (default)
|
|
285
|
+
apply_patch=True, # Enhanced parsing (default)
|
|
286
|
+
capture_xml=False # Disable debug mode (default)
|
|
287
|
+
)
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Notes
|
|
291
|
+
|
|
292
|
+
- **Authentication:** This library uses **WS-UsernameToken with Digest** authentication by default, which is the standard for ONVIF devices.
|
|
293
|
+
- **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.
|
|
294
|
+
- **XML Capture:** Only use `capture_xml=True` during development/debugging as it increases memory usage and may expose sensitive data in logs.
|
|
295
|
+
- **Cache Location:** Disk cache (when using `CacheMode.DB` or `CacheMode.ALL`) is stored in `~/.onvif-python/onvif_zeep_cache.sqlite`.
|
|
296
|
+
|
|
112
297
|
## Service Discovery: Understanding Device Capabilities
|
|
113
298
|
|
|
114
299
|
> [!WARNING]
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from .client import ONVIFClient
|
|
4
4
|
from .operator import ONVIFOperator, CacheMode
|
|
5
|
-
from .utils import ONVIFWSDL, ONVIFOperationException
|
|
6
|
-
from .utils.zeep import apply_patch, remove_patch, is_patched
|
|
5
|
+
from .utils import ONVIFWSDL, ONVIFOperationException, ONVIFErrorHandler, ZeepPatcher
|
|
7
6
|
|
|
8
7
|
__all__ = [
|
|
9
8
|
"ONVIFClient",
|
|
@@ -11,7 +10,6 @@ __all__ = [
|
|
|
11
10
|
"CacheMode",
|
|
12
11
|
"ONVIFWSDL",
|
|
13
12
|
"ONVIFOperationException",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"is_patched",
|
|
13
|
+
"ONVIFErrorHandler",
|
|
14
|
+
"ZeepPatcher",
|
|
17
15
|
]
|
|
@@ -41,12 +41,7 @@ from .services import (
|
|
|
41
41
|
MediaSigning,
|
|
42
42
|
)
|
|
43
43
|
from .operator import CacheMode
|
|
44
|
-
from .utils
|
|
45
|
-
apply_patch as _apply_zeep_patch,
|
|
46
|
-
remove_patch as _remove_zeep_patch,
|
|
47
|
-
)
|
|
48
|
-
from .utils.wsdl import ONVIFWSDL
|
|
49
|
-
from .utils.xml_capture import XMLCapturePlugin
|
|
44
|
+
from .utils import ONVIFWSDL, ZeepPatcher, XMLCapturePlugin
|
|
50
45
|
|
|
51
46
|
|
|
52
47
|
class ONVIFClient:
|
|
@@ -67,9 +62,9 @@ class ONVIFClient:
|
|
|
67
62
|
|
|
68
63
|
# Apply or remove zeep patch based on user preference
|
|
69
64
|
if apply_patch:
|
|
70
|
-
|
|
65
|
+
ZeepPatcher.apply_patch()
|
|
71
66
|
else:
|
|
72
|
-
|
|
67
|
+
ZeepPatcher.remove_patch()
|
|
73
68
|
|
|
74
69
|
# Initialize XML capture plugin if requested
|
|
75
70
|
self.xml_plugin = None
|
|
@@ -10,8 +10,7 @@ from zeep.cache import SqliteCache
|
|
|
10
10
|
from zeep.exceptions import Fault
|
|
11
11
|
from zeep.wsse.username import UsernameToken
|
|
12
12
|
|
|
13
|
-
from .utils import ONVIFOperationException
|
|
14
|
-
from .utils.zeep import flatten_xsd_any_fields
|
|
13
|
+
from .utils import ONVIFOperationException, ZeepPatcher
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
class CacheMode(Enum):
|
|
@@ -117,7 +116,7 @@ class ONVIFOperator:
|
|
|
117
116
|
raise ValueError("Bindings must be set according to the WSDL service")
|
|
118
117
|
|
|
119
118
|
self.service = self.client.create_service(binding, self.address)
|
|
120
|
-
logging.debug(f"
|
|
119
|
+
# logging.debug(f"ONVIFOperator initialized {binding} at {self.address}")
|
|
121
120
|
|
|
122
121
|
def call(self, method: str, *args, **kwargs):
|
|
123
122
|
try:
|
|
@@ -129,7 +128,7 @@ class ONVIFOperator:
|
|
|
129
128
|
result = func(*args, **kwargs)
|
|
130
129
|
# Post-process to flatten xsd:any fields if enabled (> v0.0.4 patch)
|
|
131
130
|
if self.apply_flatten:
|
|
132
|
-
return flatten_xsd_any_fields(result)
|
|
131
|
+
return ZeepPatcher.flatten_xsd_any_fields(result)
|
|
133
132
|
return result
|
|
134
133
|
except Fault as e:
|
|
135
134
|
# logging.error(f"SOAP Fault in {method}: {e}")
|
|
@@ -28,7 +28,9 @@ class Imaging:
|
|
|
28
28
|
"GetImagingSettings", VideoSourceToken=VideoSourceToken
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
-
def SetImagingSettings(
|
|
31
|
+
def SetImagingSettings(
|
|
32
|
+
self, VideoSourceToken, ImagingSettings, ForcePersistence=None
|
|
33
|
+
):
|
|
32
34
|
return self.operator.call(
|
|
33
35
|
"SetImagingSettings",
|
|
34
36
|
VideoSourceToken=VideoSourceToken,
|
|
@@ -10,7 +10,7 @@ class Provisioning:
|
|
|
10
10
|
# - ONVIF Release 16.12 (December 2016) Release Notes
|
|
11
11
|
# - ProvisioningBinding (ver10/provisioning/wsdl/provisioning.wsdl)
|
|
12
12
|
# - Operations: https://developer.onvif.org/pub/specs/branches/development/wsdl/ver10/provisioning/wsdl/provisioning.wsdl
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
definition = ONVIFWSDL.get_definition("provisioning")
|
|
15
15
|
self.operator = ONVIFOperator(
|
|
16
16
|
definition["path"],
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .wsdl import ONVIFWSDL
|
|
2
|
+
from .exceptions import ONVIFOperationException
|
|
3
|
+
from .zeep import ZeepPatcher
|
|
4
|
+
from .xml_capture import XMLCapturePlugin
|
|
5
|
+
from .error_handlers import ONVIFErrorHandler
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"ONVIFWSDL",
|
|
10
|
+
"ONVIFOperationException",
|
|
11
|
+
"ZeepPatcher",
|
|
12
|
+
"XMLCapturePlugin",
|
|
13
|
+
"ONVIFErrorHandler",
|
|
14
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# onvif/utils/error_handlers.py
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from zeep.exceptions import Fault
|
|
5
|
+
from .exceptions import ONVIFOperationException
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ONVIFErrorHandler:
|
|
9
|
+
"""
|
|
10
|
+
Error handling utilities for ONVIF operations.
|
|
11
|
+
|
|
12
|
+
Provides static methods to handle ONVIF errors gracefully,
|
|
13
|
+
especially ActionNotSupported SOAP faults.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def is_action_not_supported(exception):
|
|
18
|
+
"""Check if an ONVIFOperationException is caused by ActionNotSupported SOAP fault."""
|
|
19
|
+
try:
|
|
20
|
+
# Handle ONVIFOperationException
|
|
21
|
+
if isinstance(exception, ONVIFOperationException):
|
|
22
|
+
original = exception.original_exception
|
|
23
|
+
else:
|
|
24
|
+
original = exception
|
|
25
|
+
|
|
26
|
+
# Check if it's a Fault with subcodes
|
|
27
|
+
if isinstance(original, Fault):
|
|
28
|
+
subcodes = getattr(original, "subcodes", None)
|
|
29
|
+
if subcodes:
|
|
30
|
+
for subcode in subcodes:
|
|
31
|
+
if hasattr(subcode, "localname"):
|
|
32
|
+
if subcode.localname == "ActionNotSupported":
|
|
33
|
+
return True
|
|
34
|
+
elif "ActionNotSupported" in str(subcode):
|
|
35
|
+
return True
|
|
36
|
+
except:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def safe_call(func, default=None, ignore_unsupported=True, log_error=True):
|
|
43
|
+
"""Safely call an ONVIF operation with graceful error handling."""
|
|
44
|
+
try:
|
|
45
|
+
return func()
|
|
46
|
+
except ONVIFOperationException as e:
|
|
47
|
+
# Check if it's ActionNotSupported error
|
|
48
|
+
if ignore_unsupported and ONVIFErrorHandler.is_action_not_supported(e):
|
|
49
|
+
# if log_error:
|
|
50
|
+
# logging.warning(f"Operation not supported: {e.operation}")
|
|
51
|
+
return default
|
|
52
|
+
# Re-raise other errors
|
|
53
|
+
raise
|
|
54
|
+
except Exception as e:
|
|
55
|
+
# Wrap unexpected exceptions
|
|
56
|
+
# if log_error:
|
|
57
|
+
# logging.error(f"Unexpected error in safe_call: {e}")
|
|
58
|
+
raise
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def ignore_unsupported(func):
|
|
62
|
+
"""
|
|
63
|
+
Decorator to ignore ActionNotSupported SOAP faults.
|
|
64
|
+
Returns None for unsupported operations, raises other exceptions.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def wrapper(*args, **kwargs):
|
|
68
|
+
try:
|
|
69
|
+
return func(*args, **kwargs)
|
|
70
|
+
except ONVIFOperationException as e:
|
|
71
|
+
if ONVIFErrorHandler.is_action_not_supported(e):
|
|
72
|
+
# logging.warning(f"Operation not supported: {e.operation}")
|
|
73
|
+
return None
|
|
74
|
+
raise
|
|
75
|
+
|
|
76
|
+
return wrapper
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# onvif/utils/exceptions.py
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
(1) SOAP Errors
|
|
5
|
+
(2) Transport/Protocol Errors
|
|
6
|
+
(3) Application Errors
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from zeep.exceptions import Fault
|
|
10
|
+
import requests
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ONVIFOperationException(Exception):
|
|
14
|
+
def __init__(self, operation, original_exception):
|
|
15
|
+
self.operation = operation
|
|
16
|
+
self.original_exception = original_exception
|
|
17
|
+
|
|
18
|
+
if isinstance(original_exception, Fault):
|
|
19
|
+
# SOAP-level error
|
|
20
|
+
category = "SOAP Error"
|
|
21
|
+
|
|
22
|
+
# Extract fault information (supports both SOAP 1.1 and 1.2)
|
|
23
|
+
code = getattr(original_exception, "code", None) or getattr(
|
|
24
|
+
original_exception, "faultcode", None
|
|
25
|
+
)
|
|
26
|
+
subcodes = getattr(original_exception, "subcodes", None)
|
|
27
|
+
message = getattr(original_exception, "message", None) or str(
|
|
28
|
+
original_exception
|
|
29
|
+
)
|
|
30
|
+
detail = getattr(original_exception, "detail", None)
|
|
31
|
+
|
|
32
|
+
# Convert subcodes from QName objects to readable strings
|
|
33
|
+
if subcodes:
|
|
34
|
+
try:
|
|
35
|
+
# subcodes is a list of lxml.etree.QName objects
|
|
36
|
+
# QName objects have .localname (e.g., "ActionNotSupported")
|
|
37
|
+
# and .namespace (e.g., "http://www.onvif.org/ver10/error")
|
|
38
|
+
subcode_strings = []
|
|
39
|
+
for qname in subcodes:
|
|
40
|
+
if hasattr(qname, "localname"):
|
|
41
|
+
# Use only the local name without namespace
|
|
42
|
+
subcode_strings.append(qname.localname)
|
|
43
|
+
else:
|
|
44
|
+
# Fallback to string representation
|
|
45
|
+
subcode_strings.append(str(qname))
|
|
46
|
+
subcodes = ", ".join(subcode_strings)
|
|
47
|
+
except:
|
|
48
|
+
subcodes = str(subcodes)
|
|
49
|
+
|
|
50
|
+
# Build comprehensive error message
|
|
51
|
+
parts = [f"code={code}"]
|
|
52
|
+
if subcodes:
|
|
53
|
+
parts.append(f"subcode={subcodes}")
|
|
54
|
+
if message:
|
|
55
|
+
parts.append(f"msg={message}")
|
|
56
|
+
if detail:
|
|
57
|
+
parts.append(f"detail={detail}")
|
|
58
|
+
|
|
59
|
+
msg = f"{category}: {', '.join(parts)}"
|
|
60
|
+
elif isinstance(original_exception, requests.exceptions.RequestException):
|
|
61
|
+
# Transport/Protocol error
|
|
62
|
+
category = "Protocol Error"
|
|
63
|
+
msg = f"{category}: {str(original_exception)}"
|
|
64
|
+
else:
|
|
65
|
+
# Application or generic error
|
|
66
|
+
category = "Application Error"
|
|
67
|
+
msg = f"{category}: {str(original_exception)}"
|
|
68
|
+
|
|
69
|
+
super().__init__(f"ONVIF operation '{operation}' failed: {msg}")
|