onvif-python 0.0.8__tar.gz → 0.0.9__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.8/onvif_python.egg-info → onvif_python-0.0.9}/PKG-INFO +62 -31
- {onvif_python-0.0.8 → onvif_python-0.0.9}/README.md +61 -27
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/client.py +113 -43
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/operator.py +3 -3
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/uplink.py +1 -1
- {onvif_python-0.0.8 → onvif_python-0.0.9/onvif_python.egg-info}/PKG-INFO +62 -31
- {onvif_python-0.0.8 → onvif_python-0.0.9}/pyproject.toml +1 -4
- {onvif_python-0.0.8 → onvif_python-0.0.9}/LICENSE.md +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/MANIFEST.in +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/__init__.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/__init__.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/accesscontrol.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/accessrules.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/actionengine.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/analytics/analytics.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/analytics/ruleengine.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/analyticsdevice.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/appmgmt.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/authenticationbehavior.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/credential.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/deviceio.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/devicemgmt.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/display.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/doorcontrol.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/events/events.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/events/notification.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/events/pullpoint.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/events/subscription.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/imaging.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/media.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/media2.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/provisioning.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/ptz.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/receiver.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/recording.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/replay.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/schedule.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/search.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/security/advancedsecurity.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/security/authorizationserver.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/security/dot1x.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/security/jwt.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/security/keystore.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/security/mediasigning.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/security/tlsserver.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/services/thermal.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/utils/__init__.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/utils/error_handlers.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/utils/exceptions.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/utils/wsdl.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/utils/xml_capture.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/utils/zeep.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/display/display.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/display.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/receiver.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/recording.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/replay.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/schema/common.xsd +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/search.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/analytics/index.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif_python.egg-info/SOURCES.txt +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif_python.egg-info/dependency_links.txt +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif_python.egg-info/requires.txt +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/onvif_python.egg-info/top_level.txt +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/setup.cfg +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/setup.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/tests/test_client.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/tests/test_core.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/tests/test_exceptions.py +0 -0
- {onvif_python-0.0.8 → onvif_python-0.0.9}/tests/test_wsdl_map.py +0 -0
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: onvif-python
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.9
|
|
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
|
|
7
7
|
License-Expression: MIT
|
|
8
|
-
Project-URL: Homepage, https://github.com/nirsimetri/onvif-python
|
|
9
|
-
Project-URL: Issues, https://github.com/nirsimetri/onvif-python/issues
|
|
10
8
|
Project-URL: Documentation, https://deepwiki.com/nirsimetri/onvif-python
|
|
11
|
-
Project-URL: Repository, https://github.com/nirsimetri/onvif-python.git
|
|
12
9
|
Project-URL: Changelog, https://github.com/nirsimetri/onvif-python/releases
|
|
13
10
|
Keywords: onvif,Camera,onvif-python,ip-camera,cctv,soap,surveillance,cctv cameras,soap-client,onvif-client,onvif-library,onvif-specifications
|
|
14
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
@@ -43,9 +40,9 @@ Dynamic: license-file
|
|
|
43
40
|
|
|
44
41
|
[](https://github.com/nirsimetri/onvif-python?tab=MIT-1-ov-file)
|
|
45
42
|
[](https://deepwiki.com/nirsimetri/onvif-python)
|
|
46
|
-
[](https://github.com/nirsimetri/onvif-python/releases)
|
|
47
44
|
<br>
|
|
48
|
-
[](https://pypi.org/project/onvif-python/)
|
|
49
46
|
[](https://clickpy.clickhouse.com/dashboard/onvif-python)
|
|
50
47
|
|
|
51
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.
|
|
@@ -354,7 +351,7 @@ client = ONVIFClient(
|
|
|
354
351
|
## Service Discovery: Understanding Device Capabilities
|
|
355
352
|
|
|
356
353
|
> [!WARNING]
|
|
357
|
-
> Before performing any operations on an ONVIF device, it is highly recommended to discover which services are available and supported by the device. This library automatically
|
|
354
|
+
> Before performing any operations on an ONVIF device, it is highly recommended to discover which services are available and supported by the device. This library automatically performs comprehensive service discovery during initialization using a robust fallback mechanism.
|
|
358
355
|
|
|
359
356
|
**Why discover device services?**
|
|
360
357
|
|
|
@@ -365,50 +362,84 @@ client = ONVIFClient(
|
|
|
365
362
|
|
|
366
363
|
**How service discovery works in this library:**
|
|
367
364
|
|
|
368
|
-
The `ONVIFClient`
|
|
365
|
+
The `ONVIFClient` uses a **3-tier discovery approach** to maximize device compatibility:
|
|
366
|
+
|
|
367
|
+
1. **GetServices (Preferred)** - Tries `GetServices` first for detailed service information
|
|
368
|
+
2. **GetCapabilities (Fallback)** - Falls back to `GetCapabilities` if `GetServices` is not supported
|
|
369
|
+
3. **Default URLs (Final Fallback)** - Uses standard ONVIF URLs as last resort
|
|
369
370
|
|
|
370
371
|
```python
|
|
371
372
|
from onvif import ONVIFClient
|
|
372
373
|
|
|
373
374
|
client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
|
|
374
375
|
|
|
375
|
-
#
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
376
|
+
# Check what discovery method was used
|
|
377
|
+
if client.services:
|
|
378
|
+
print("Service discovery: GetServices (preferred)")
|
|
379
|
+
print("Discovered services:", len(client.services))
|
|
380
|
+
print("Service map:", client._service_map)
|
|
381
|
+
elif client.capabilities:
|
|
382
|
+
print("Service discovery: GetCapabilities (fallback)")
|
|
383
|
+
print("Available capabilities:", client.capabilities)
|
|
384
|
+
else:
|
|
385
|
+
print("Service discovery: Using default URLs")
|
|
382
386
|
```
|
|
383
387
|
|
|
384
|
-
**
|
|
388
|
+
**Why this approach?**
|
|
389
|
+
|
|
390
|
+
- **GetServices** provides the most accurate and detailed service information, but it's **optional** in the ONVIF specification
|
|
391
|
+
- **GetCapabilities** is **mandatory** for all ONVIF-compliant devices, ensuring broader compatibility
|
|
392
|
+
- **Default URLs** guarantee basic connectivity even with non-compliant devices
|
|
385
393
|
|
|
386
|
-
|
|
394
|
+
**Get detailed service information:**
|
|
395
|
+
|
|
396
|
+
If you need comprehensive service details, you can manually call `GetServices` with capabilities:
|
|
387
397
|
|
|
388
398
|
```python
|
|
389
399
|
device = client.devicemgmt()
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
400
|
+
|
|
401
|
+
try:
|
|
402
|
+
# Try to get detailed service information
|
|
403
|
+
services = device.GetServices(IncludeCapability=True)
|
|
404
|
+
for service in services:
|
|
405
|
+
print(f"Service: {service.Namespace}")
|
|
406
|
+
print(f"XAddr: {service.XAddr}")
|
|
407
|
+
if hasattr(service, 'Capabilities'):
|
|
408
|
+
print(f"Capabilities: {service.Capabilities}")
|
|
409
|
+
except Exception:
|
|
410
|
+
# Fallback to GetCapabilities for legacy devices
|
|
411
|
+
capabilities = device.GetCapabilities()
|
|
412
|
+
print("Capabilities:", capabilities)
|
|
398
413
|
```
|
|
399
414
|
|
|
400
|
-
**
|
|
415
|
+
**Access capabilities information:**
|
|
401
416
|
|
|
402
|
-
|
|
417
|
+
When using `GetCapabilities` fallback, you can access capability information:
|
|
403
418
|
|
|
404
419
|
```python
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
420
|
+
device = client.devicemgmt()
|
|
421
|
+
|
|
422
|
+
try:
|
|
423
|
+
# Get capabilities information from device
|
|
424
|
+
capabilities = device.GetCapabilities()
|
|
425
|
+
|
|
426
|
+
# Main services (always available)
|
|
427
|
+
print("Media XAddr:", getattr(capabilities.Media, 'XAddr', 'Not available'))
|
|
428
|
+
print("PTZ XAddr:", getattr(capabilities.PTZ, 'XAddr', 'Not available'))
|
|
429
|
+
|
|
430
|
+
# Extension services (device-dependent)
|
|
431
|
+
ext = getattr(capabilities, 'Extension', None)
|
|
432
|
+
if ext:
|
|
433
|
+
print("DeviceIO XAddr:", getattr(ext.DeviceIO, 'XAddr', 'Not available'))
|
|
434
|
+
print("Recording XAddr:", getattr(ext.Recording, 'XAddr', 'Not available'))
|
|
435
|
+
print("Search XAddr:", getattr(ext.Search, 'XAddr', 'Not available'))
|
|
436
|
+
print("Replay XAddr:", getattr(ext.Replay, 'XAddr', 'Not available'))
|
|
437
|
+
except Exception as e:
|
|
438
|
+
print(f"Error getting capabilities: {e}")
|
|
408
439
|
```
|
|
409
440
|
|
|
410
441
|
> [!TIP]
|
|
411
|
-
> The library handles service discovery automatically
|
|
442
|
+
> The library handles service discovery automatically with intelligent fallback. You typically don't need to call discovery methods manually unless you need detailed capability information or want to refresh the service list after device configuration changes.
|
|
412
443
|
|
|
413
444
|
## Tested Devices
|
|
414
445
|
|
|
@@ -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.
|
|
@@ -313,7 +313,7 @@ client = ONVIFClient(
|
|
|
313
313
|
## Service Discovery: Understanding Device Capabilities
|
|
314
314
|
|
|
315
315
|
> [!WARNING]
|
|
316
|
-
> Before performing any operations on an ONVIF device, it is highly recommended to discover which services are available and supported by the device. This library automatically
|
|
316
|
+
> Before performing any operations on an ONVIF device, it is highly recommended to discover which services are available and supported by the device. This library automatically performs comprehensive service discovery during initialization using a robust fallback mechanism.
|
|
317
317
|
|
|
318
318
|
**Why discover device services?**
|
|
319
319
|
|
|
@@ -324,50 +324,84 @@ client = ONVIFClient(
|
|
|
324
324
|
|
|
325
325
|
**How service discovery works in this library:**
|
|
326
326
|
|
|
327
|
-
The `ONVIFClient`
|
|
327
|
+
The `ONVIFClient` uses a **3-tier discovery approach** to maximize device compatibility:
|
|
328
|
+
|
|
329
|
+
1. **GetServices (Preferred)** - Tries `GetServices` first for detailed service information
|
|
330
|
+
2. **GetCapabilities (Fallback)** - Falls back to `GetCapabilities` if `GetServices` is not supported
|
|
331
|
+
3. **Default URLs (Final Fallback)** - Uses standard ONVIF URLs as last resort
|
|
328
332
|
|
|
329
333
|
```python
|
|
330
334
|
from onvif import ONVIFClient
|
|
331
335
|
|
|
332
336
|
client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
|
|
333
337
|
|
|
334
|
-
#
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
338
|
+
# Check what discovery method was used
|
|
339
|
+
if client.services:
|
|
340
|
+
print("Service discovery: GetServices (preferred)")
|
|
341
|
+
print("Discovered services:", len(client.services))
|
|
342
|
+
print("Service map:", client._service_map)
|
|
343
|
+
elif client.capabilities:
|
|
344
|
+
print("Service discovery: GetCapabilities (fallback)")
|
|
345
|
+
print("Available capabilities:", client.capabilities)
|
|
346
|
+
else:
|
|
347
|
+
print("Service discovery: Using default URLs")
|
|
341
348
|
```
|
|
342
349
|
|
|
343
|
-
**
|
|
350
|
+
**Why this approach?**
|
|
351
|
+
|
|
352
|
+
- **GetServices** provides the most accurate and detailed service information, but it's **optional** in the ONVIF specification
|
|
353
|
+
- **GetCapabilities** is **mandatory** for all ONVIF-compliant devices, ensuring broader compatibility
|
|
354
|
+
- **Default URLs** guarantee basic connectivity even with non-compliant devices
|
|
344
355
|
|
|
345
|
-
|
|
356
|
+
**Get detailed service information:**
|
|
357
|
+
|
|
358
|
+
If you need comprehensive service details, you can manually call `GetServices` with capabilities:
|
|
346
359
|
|
|
347
360
|
```python
|
|
348
361
|
device = client.devicemgmt()
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
362
|
+
|
|
363
|
+
try:
|
|
364
|
+
# Try to get detailed service information
|
|
365
|
+
services = device.GetServices(IncludeCapability=True)
|
|
366
|
+
for service in services:
|
|
367
|
+
print(f"Service: {service.Namespace}")
|
|
368
|
+
print(f"XAddr: {service.XAddr}")
|
|
369
|
+
if hasattr(service, 'Capabilities'):
|
|
370
|
+
print(f"Capabilities: {service.Capabilities}")
|
|
371
|
+
except Exception:
|
|
372
|
+
# Fallback to GetCapabilities for legacy devices
|
|
373
|
+
capabilities = device.GetCapabilities()
|
|
374
|
+
print("Capabilities:", capabilities)
|
|
357
375
|
```
|
|
358
376
|
|
|
359
|
-
**
|
|
377
|
+
**Access capabilities information:**
|
|
360
378
|
|
|
361
|
-
|
|
379
|
+
When using `GetCapabilities` fallback, you can access capability information:
|
|
362
380
|
|
|
363
381
|
```python
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
382
|
+
device = client.devicemgmt()
|
|
383
|
+
|
|
384
|
+
try:
|
|
385
|
+
# Get capabilities information from device
|
|
386
|
+
capabilities = device.GetCapabilities()
|
|
387
|
+
|
|
388
|
+
# Main services (always available)
|
|
389
|
+
print("Media XAddr:", getattr(capabilities.Media, 'XAddr', 'Not available'))
|
|
390
|
+
print("PTZ XAddr:", getattr(capabilities.PTZ, 'XAddr', 'Not available'))
|
|
391
|
+
|
|
392
|
+
# Extension services (device-dependent)
|
|
393
|
+
ext = getattr(capabilities, 'Extension', None)
|
|
394
|
+
if ext:
|
|
395
|
+
print("DeviceIO XAddr:", getattr(ext.DeviceIO, 'XAddr', 'Not available'))
|
|
396
|
+
print("Recording XAddr:", getattr(ext.Recording, 'XAddr', 'Not available'))
|
|
397
|
+
print("Search XAddr:", getattr(ext.Search, 'XAddr', 'Not available'))
|
|
398
|
+
print("Replay XAddr:", getattr(ext.Replay, 'XAddr', 'Not available'))
|
|
399
|
+
except Exception as e:
|
|
400
|
+
print(f"Error getting capabilities: {e}")
|
|
367
401
|
```
|
|
368
402
|
|
|
369
403
|
> [!TIP]
|
|
370
|
-
> The library handles service discovery automatically
|
|
404
|
+
> The library handles service discovery automatically with intelligent fallback. You typically don't need to call discovery methods manually unless you need detailed capability information or want to refresh the service list after device configuration changes.
|
|
371
405
|
|
|
372
406
|
## Tested Devices
|
|
373
407
|
|
|
@@ -58,8 +58,6 @@ class ONVIFClient:
|
|
|
58
58
|
apply_patch=True,
|
|
59
59
|
capture_xml=False,
|
|
60
60
|
):
|
|
61
|
-
self.apply_patch = apply_patch
|
|
62
|
-
|
|
63
61
|
# Apply or remove zeep patch based on user preference
|
|
64
62
|
if apply_patch:
|
|
65
63
|
ZeepPatcher.apply_patch()
|
|
@@ -81,22 +79,37 @@ class ONVIFClient:
|
|
|
81
79
|
"cache": cache,
|
|
82
80
|
"use_https": use_https,
|
|
83
81
|
"verify_ssl": verify_ssl,
|
|
84
|
-
"
|
|
82
|
+
"apply_patch": apply_patch,
|
|
85
83
|
"plugins": [self.xml_plugin] if self.xml_plugin else None,
|
|
86
84
|
}
|
|
87
85
|
|
|
88
86
|
# Device Management (Core) service is always available
|
|
89
87
|
self._devicemgmt = Device(**self.common_args)
|
|
90
88
|
|
|
91
|
-
#
|
|
92
|
-
self.services =
|
|
89
|
+
# Try to retrieve device services and create namespace -> XAddr mapping
|
|
90
|
+
self.services = None
|
|
93
91
|
self._service_map = {}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
|
|
93
|
+
# Temporary variable to hold capabilities
|
|
94
|
+
self.capabilities = None
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
# Try GetServices first (preferred method)
|
|
98
|
+
self.services = self._devicemgmt.GetServices(IncludeCapability=False)
|
|
99
|
+
for service in self.services:
|
|
100
|
+
namespace = getattr(service, "Namespace", None)
|
|
101
|
+
xaddr = getattr(service, "XAddr", None)
|
|
97
102
|
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
if namespace and xaddr:
|
|
104
|
+
self._service_map[namespace] = xaddr
|
|
105
|
+
|
|
106
|
+
except Exception:
|
|
107
|
+
# Fallback to GetCapabilities if GetServices is not supported on device
|
|
108
|
+
try:
|
|
109
|
+
self.capabilities = self._devicemgmt.GetCapabilities(Category="All")
|
|
110
|
+
except Exception:
|
|
111
|
+
# If both fail, we'll use default URLs
|
|
112
|
+
pass
|
|
100
113
|
|
|
101
114
|
# Lazy init for other services
|
|
102
115
|
|
|
@@ -158,49 +171,106 @@ class ONVIFClient:
|
|
|
158
171
|
|
|
159
172
|
def _get_xaddr(self, service_name: str, service_path: str):
|
|
160
173
|
"""
|
|
161
|
-
Resolve XAddr
|
|
162
|
-
|
|
174
|
+
Resolve XAddr for ONVIF services using a comprehensive 3-tier discovery approach.
|
|
175
|
+
|
|
176
|
+
1. GetServices: Try to resolve from GetServices response using namespace mapping
|
|
177
|
+
2. GetCapabilities: Fall back to GetCapabilities response with multiple lookup strategies:
|
|
178
|
+
- Direct capabilities.service_path
|
|
179
|
+
- Extension capabilities.Extension.service_path
|
|
180
|
+
- Nested Extension capabilities.Extension.Extension.service_path
|
|
181
|
+
3. Default URL: Generate default ONVIF URL as final fallback
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
service_name: Internal service name (e.g., 'imaging', 'media', 'deviceio')
|
|
185
|
+
service_path: ONVIF service path (e.g., 'Imaging', 'Media', 'DeviceIO')
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
str: The resolved XAddr URL for the service
|
|
189
|
+
|
|
190
|
+
Notes:
|
|
191
|
+
- GetServices is the preferred method as it provides the most accurate service endpoints.
|
|
192
|
+
But not all devices implement it because it's optional in the ONVIF spec.
|
|
193
|
+
- GetCapabilities lookup tries multiple strategies to maximize chances of finding the XAddr.
|
|
194
|
+
And GetCapabilities is mandatory for ONVIF devices, so it's more widely supported.
|
|
195
|
+
- Fallback to default URL ensures basic connectivity even if the device lacks proper service discovery.
|
|
163
196
|
"""
|
|
164
|
-
# Get the namespace for this service from WSDL_MAP
|
|
165
|
-
try:
|
|
166
|
-
# Try to get the service definition from WSDL_MAP
|
|
167
|
-
# Most services use ver10, some use ver20
|
|
168
|
-
wsdl_def = None
|
|
169
197
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
198
|
+
# First try to get from GetServices mapping
|
|
199
|
+
if self.services:
|
|
200
|
+
# Get the namespace for this service from WSDL_MAP
|
|
201
|
+
try:
|
|
202
|
+
# Try to get the service definition from WSDL_MAP
|
|
203
|
+
# Most services use ver10, some use ver20
|
|
204
|
+
wsdl_def = None
|
|
205
|
+
|
|
206
|
+
# Try ver10 first, then ver20
|
|
207
|
+
if service_name in ONVIFWSDL.WSDL_MAP:
|
|
208
|
+
if "ver10" in ONVIFWSDL.WSDL_MAP[service_name]:
|
|
209
|
+
wsdl_def = ONVIFWSDL.WSDL_MAP[service_name]["ver10"]
|
|
210
|
+
elif "ver20" in ONVIFWSDL.WSDL_MAP[service_name]:
|
|
211
|
+
wsdl_def = ONVIFWSDL.WSDL_MAP[service_name]["ver20"]
|
|
212
|
+
|
|
213
|
+
if wsdl_def:
|
|
214
|
+
namespace = wsdl_def["namespace"]
|
|
215
|
+
xaddr = self._service_map.get(namespace)
|
|
216
|
+
|
|
217
|
+
if xaddr:
|
|
218
|
+
# Rewrite host/port if needed
|
|
219
|
+
rewritten = self._rewrite_xaddr_if_needed(xaddr)
|
|
220
|
+
return rewritten
|
|
221
|
+
except Exception:
|
|
222
|
+
pass
|
|
176
223
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
224
|
+
# If not found in service map and we have capabilities, try to get it dynamically from GetCapabilities
|
|
225
|
+
if self.capabilities:
|
|
226
|
+
try:
|
|
227
|
+
svc = getattr(self.capabilities, service_path, None)
|
|
228
|
+
# Step 1: check direct attribute capabilities.service_path (e.g. capabilities.Media)
|
|
229
|
+
if svc and hasattr(svc, "XAddr"):
|
|
230
|
+
xaddr = svc.XAddr
|
|
231
|
+
else:
|
|
232
|
+
# Step 2: try capabilities.Extension.service_path (e.g. capabilities.Extension.DeviceIO)
|
|
233
|
+
ext = getattr(self.capabilities, "Extension", None)
|
|
234
|
+
if ext and hasattr(ext, service_path):
|
|
235
|
+
svc = getattr(ext, service_path, None)
|
|
236
|
+
xaddr = getattr(svc, "XAddr", None) if svc else None
|
|
237
|
+
else:
|
|
238
|
+
# Step 3: try capabilities.Extension.Extension.service_path (e.g. capabilities.Extension.Extension.Provisioning)
|
|
239
|
+
ext_ext = getattr(ext, "Extension", None)
|
|
240
|
+
if ext_ext and hasattr(ext_ext, service_path):
|
|
241
|
+
svc = getattr(ext_ext, service_path, None)
|
|
242
|
+
xaddr = getattr(svc, "XAddr", None) if svc else None
|
|
180
243
|
|
|
181
244
|
if xaddr:
|
|
182
245
|
# Rewrite host/port if needed
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
connect_port = self.common_args["port"]
|
|
188
|
-
|
|
189
|
-
if (device_host != connect_host) or (device_port != connect_port):
|
|
190
|
-
protocol = "https" if self.common_args["use_https"] else "http"
|
|
191
|
-
new_netloc = f"{connect_host}:{connect_port}"
|
|
192
|
-
rewritten = urlunparse(
|
|
193
|
-
(protocol, new_netloc, parsed.path, "", "", "")
|
|
194
|
-
)
|
|
195
|
-
return rewritten
|
|
196
|
-
return xaddr
|
|
197
|
-
except Exception:
|
|
198
|
-
pass
|
|
246
|
+
rewritten = self._rewrite_xaddr_if_needed(xaddr)
|
|
247
|
+
return rewritten
|
|
248
|
+
except Exception:
|
|
249
|
+
pass
|
|
199
250
|
|
|
200
251
|
# Fallback to default URL
|
|
201
252
|
protocol = "https" if self.common_args["use_https"] else "http"
|
|
202
253
|
return f"{protocol}://{self.common_args['host']}:{self.common_args['port']}/onvif/{service_path}"
|
|
203
254
|
|
|
255
|
+
def _rewrite_xaddr_if_needed(self, xaddr: str):
|
|
256
|
+
"""
|
|
257
|
+
Rewrite XAddr to use client's host/port if different from device's.
|
|
258
|
+
"""
|
|
259
|
+
try:
|
|
260
|
+
parsed = urlparse(xaddr)
|
|
261
|
+
device_host = parsed.hostname
|
|
262
|
+
device_port = parsed.port
|
|
263
|
+
connect_host = self.common_args["host"]
|
|
264
|
+
connect_port = self.common_args["port"]
|
|
265
|
+
|
|
266
|
+
if (device_host != connect_host) or (device_port != connect_port):
|
|
267
|
+
protocol = "https" if self.common_args["use_https"] else "http"
|
|
268
|
+
new_netloc = f"{connect_host}:{connect_port}"
|
|
269
|
+
return urlunparse((protocol, new_netloc, parsed.path, "", "", ""))
|
|
270
|
+
return xaddr
|
|
271
|
+
except Exception:
|
|
272
|
+
return xaddr
|
|
273
|
+
|
|
204
274
|
# Core (Device Management)
|
|
205
275
|
|
|
206
276
|
def devicemgmt(self):
|
|
@@ -459,7 +529,7 @@ class ONVIFClient:
|
|
|
459
529
|
def search(self):
|
|
460
530
|
if self._search is None:
|
|
461
531
|
self._search = Search(
|
|
462
|
-
xaddr=self._get_xaddr("search", "
|
|
532
|
+
xaddr=self._get_xaddr("search", "Search"),
|
|
463
533
|
**self.common_args,
|
|
464
534
|
)
|
|
465
535
|
return self._search
|
|
@@ -51,7 +51,7 @@ class ONVIFOperator:
|
|
|
51
51
|
cache_path: str = None,
|
|
52
52
|
use_https: bool = False,
|
|
53
53
|
verify_ssl: bool = True,
|
|
54
|
-
|
|
54
|
+
apply_patch: bool = True,
|
|
55
55
|
plugins: list = None,
|
|
56
56
|
):
|
|
57
57
|
self.wsdl_path = wsdl_path
|
|
@@ -60,7 +60,7 @@ class ONVIFOperator:
|
|
|
60
60
|
self.username = username
|
|
61
61
|
self.password = password
|
|
62
62
|
self.timeout = timeout
|
|
63
|
-
self.
|
|
63
|
+
self.apply_patch = apply_patch
|
|
64
64
|
|
|
65
65
|
if xaddr:
|
|
66
66
|
self.address = xaddr
|
|
@@ -127,7 +127,7 @@ class ONVIFOperator:
|
|
|
127
127
|
try:
|
|
128
128
|
result = func(*args, **kwargs)
|
|
129
129
|
# Post-process to flatten xsd:any fields if enabled (> v0.0.4 patch)
|
|
130
|
-
if self.
|
|
130
|
+
if self.apply_patch:
|
|
131
131
|
return ZeepPatcher.flatten_xsd_any_fields(result)
|
|
132
132
|
return result
|
|
133
133
|
except Fault as e:
|
|
@@ -30,4 +30,4 @@ class Uplink:
|
|
|
30
30
|
return self.operator.call("SetUplink", Configuration=Configuration)
|
|
31
31
|
|
|
32
32
|
def DeleteUplink(self, RemoteAddress):
|
|
33
|
-
return self.operator.call("
|
|
33
|
+
return self.operator.call("DeleteUplink", RemoteAddress=RemoteAddress)
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: onvif-python
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.9
|
|
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
|
|
7
7
|
License-Expression: MIT
|
|
8
|
-
Project-URL: Homepage, https://github.com/nirsimetri/onvif-python
|
|
9
|
-
Project-URL: Issues, https://github.com/nirsimetri/onvif-python/issues
|
|
10
8
|
Project-URL: Documentation, https://deepwiki.com/nirsimetri/onvif-python
|
|
11
|
-
Project-URL: Repository, https://github.com/nirsimetri/onvif-python.git
|
|
12
9
|
Project-URL: Changelog, https://github.com/nirsimetri/onvif-python/releases
|
|
13
10
|
Keywords: onvif,Camera,onvif-python,ip-camera,cctv,soap,surveillance,cctv cameras,soap-client,onvif-client,onvif-library,onvif-specifications
|
|
14
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
@@ -43,9 +40,9 @@ Dynamic: license-file
|
|
|
43
40
|
|
|
44
41
|
[](https://github.com/nirsimetri/onvif-python?tab=MIT-1-ov-file)
|
|
45
42
|
[](https://deepwiki.com/nirsimetri/onvif-python)
|
|
46
|
-
[](https://github.com/nirsimetri/onvif-python/releases)
|
|
47
44
|
<br>
|
|
48
|
-
[](https://pypi.org/project/onvif-python/)
|
|
49
46
|
[](https://clickpy.clickhouse.com/dashboard/onvif-python)
|
|
50
47
|
|
|
51
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.
|
|
@@ -354,7 +351,7 @@ client = ONVIFClient(
|
|
|
354
351
|
## Service Discovery: Understanding Device Capabilities
|
|
355
352
|
|
|
356
353
|
> [!WARNING]
|
|
357
|
-
> Before performing any operations on an ONVIF device, it is highly recommended to discover which services are available and supported by the device. This library automatically
|
|
354
|
+
> Before performing any operations on an ONVIF device, it is highly recommended to discover which services are available and supported by the device. This library automatically performs comprehensive service discovery during initialization using a robust fallback mechanism.
|
|
358
355
|
|
|
359
356
|
**Why discover device services?**
|
|
360
357
|
|
|
@@ -365,50 +362,84 @@ client = ONVIFClient(
|
|
|
365
362
|
|
|
366
363
|
**How service discovery works in this library:**
|
|
367
364
|
|
|
368
|
-
The `ONVIFClient`
|
|
365
|
+
The `ONVIFClient` uses a **3-tier discovery approach** to maximize device compatibility:
|
|
366
|
+
|
|
367
|
+
1. **GetServices (Preferred)** - Tries `GetServices` first for detailed service information
|
|
368
|
+
2. **GetCapabilities (Fallback)** - Falls back to `GetCapabilities` if `GetServices` is not supported
|
|
369
|
+
3. **Default URLs (Final Fallback)** - Uses standard ONVIF URLs as last resort
|
|
369
370
|
|
|
370
371
|
```python
|
|
371
372
|
from onvif import ONVIFClient
|
|
372
373
|
|
|
373
374
|
client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
|
|
374
375
|
|
|
375
|
-
#
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
376
|
+
# Check what discovery method was used
|
|
377
|
+
if client.services:
|
|
378
|
+
print("Service discovery: GetServices (preferred)")
|
|
379
|
+
print("Discovered services:", len(client.services))
|
|
380
|
+
print("Service map:", client._service_map)
|
|
381
|
+
elif client.capabilities:
|
|
382
|
+
print("Service discovery: GetCapabilities (fallback)")
|
|
383
|
+
print("Available capabilities:", client.capabilities)
|
|
384
|
+
else:
|
|
385
|
+
print("Service discovery: Using default URLs")
|
|
382
386
|
```
|
|
383
387
|
|
|
384
|
-
**
|
|
388
|
+
**Why this approach?**
|
|
389
|
+
|
|
390
|
+
- **GetServices** provides the most accurate and detailed service information, but it's **optional** in the ONVIF specification
|
|
391
|
+
- **GetCapabilities** is **mandatory** for all ONVIF-compliant devices, ensuring broader compatibility
|
|
392
|
+
- **Default URLs** guarantee basic connectivity even with non-compliant devices
|
|
385
393
|
|
|
386
|
-
|
|
394
|
+
**Get detailed service information:**
|
|
395
|
+
|
|
396
|
+
If you need comprehensive service details, you can manually call `GetServices` with capabilities:
|
|
387
397
|
|
|
388
398
|
```python
|
|
389
399
|
device = client.devicemgmt()
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
400
|
+
|
|
401
|
+
try:
|
|
402
|
+
# Try to get detailed service information
|
|
403
|
+
services = device.GetServices(IncludeCapability=True)
|
|
404
|
+
for service in services:
|
|
405
|
+
print(f"Service: {service.Namespace}")
|
|
406
|
+
print(f"XAddr: {service.XAddr}")
|
|
407
|
+
if hasattr(service, 'Capabilities'):
|
|
408
|
+
print(f"Capabilities: {service.Capabilities}")
|
|
409
|
+
except Exception:
|
|
410
|
+
# Fallback to GetCapabilities for legacy devices
|
|
411
|
+
capabilities = device.GetCapabilities()
|
|
412
|
+
print("Capabilities:", capabilities)
|
|
398
413
|
```
|
|
399
414
|
|
|
400
|
-
**
|
|
415
|
+
**Access capabilities information:**
|
|
401
416
|
|
|
402
|
-
|
|
417
|
+
When using `GetCapabilities` fallback, you can access capability information:
|
|
403
418
|
|
|
404
419
|
```python
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
420
|
+
device = client.devicemgmt()
|
|
421
|
+
|
|
422
|
+
try:
|
|
423
|
+
# Get capabilities information from device
|
|
424
|
+
capabilities = device.GetCapabilities()
|
|
425
|
+
|
|
426
|
+
# Main services (always available)
|
|
427
|
+
print("Media XAddr:", getattr(capabilities.Media, 'XAddr', 'Not available'))
|
|
428
|
+
print("PTZ XAddr:", getattr(capabilities.PTZ, 'XAddr', 'Not available'))
|
|
429
|
+
|
|
430
|
+
# Extension services (device-dependent)
|
|
431
|
+
ext = getattr(capabilities, 'Extension', None)
|
|
432
|
+
if ext:
|
|
433
|
+
print("DeviceIO XAddr:", getattr(ext.DeviceIO, 'XAddr', 'Not available'))
|
|
434
|
+
print("Recording XAddr:", getattr(ext.Recording, 'XAddr', 'Not available'))
|
|
435
|
+
print("Search XAddr:", getattr(ext.Search, 'XAddr', 'Not available'))
|
|
436
|
+
print("Replay XAddr:", getattr(ext.Replay, 'XAddr', 'Not available'))
|
|
437
|
+
except Exception as e:
|
|
438
|
+
print(f"Error getting capabilities: {e}")
|
|
408
439
|
```
|
|
409
440
|
|
|
410
441
|
> [!TIP]
|
|
411
|
-
> The library handles service discovery automatically
|
|
442
|
+
> The library handles service discovery automatically with intelligent fallback. You typically don't need to call discovery methods manually unless you need detailed capability information or want to refresh the service list after device configuration changes.
|
|
412
443
|
|
|
413
444
|
## Tested Devices
|
|
414
445
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "onvif-python"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.9"
|
|
8
8
|
description = "A comprehensive and developer-friendly Python library for working with ONVIF-compliant devices"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -66,8 +66,5 @@ extend-exclude = '''
|
|
|
66
66
|
'''
|
|
67
67
|
|
|
68
68
|
[project.urls]
|
|
69
|
-
Homepage = "https://github.com/nirsimetri/onvif-python"
|
|
70
|
-
Issues = "https://github.com/nirsimetri/onvif-python/issues"
|
|
71
69
|
Documentation = "https://deepwiki.com/nirsimetri/onvif-python"
|
|
72
|
-
Repository = "https://github.com/nirsimetri/onvif-python.git"
|
|
73
70
|
Changelog = "https://github.com/nirsimetri/onvif-python/releases"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{onvif_python-0.0.8 → onvif_python-0.0.9}/onvif/wsdl/ver20/util/federated_search_documentation.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|