onvif-python 0.0.4__tar.gz → 0.0.5__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.4/onvif_python.egg-info → onvif_python-0.0.5}/PKG-INFO +49 -16
- {onvif_python-0.0.4 → onvif_python-0.0.5}/README.md +48 -15
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/__init__.py +2 -2
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/client.py +56 -41
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/operator.py +1 -1
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/devicemgmt.py +1 -1
- onvif_python-0.0.5/onvif/utils/zeep.py +272 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5/onvif_python.egg-info}/PKG-INFO +49 -16
- {onvif_python-0.0.4 → onvif_python-0.0.5}/pyproject.toml +1 -1
- onvif_python-0.0.4/onvif/utils/zeep.py +0 -230
- {onvif_python-0.0.4 → onvif_python-0.0.5}/LICENSE.md +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/MANIFEST.in +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/__init__.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/accesscontrol.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/accessrules.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/actionengine.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/analytics/analytics.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/analytics/ruleengine.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/analyticsdevice.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/appmgmt.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/authenticationbehavior.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/credential.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/deviceio.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/display.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/doorcontrol.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/events/events.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/events/notification.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/events/pullpoint.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/events/subscription.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/imaging.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/media.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/media2.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/provisioning.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/ptz.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/receiver.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/recording.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/replay.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/schedule.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/search.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/security/advancedsecurity.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/security/authorizationserver.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/security/dot1x.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/security/jwt.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/security/keystore.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/security/mediasigning.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/security/tlsserver.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/thermal.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/services/uplink.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/utils/__init__.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/utils/exceptions.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/utils/wsdl.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/display/display.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/display.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/receiver.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/recording.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/replay.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/schema/common.xsd +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/search.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/analytics/index.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif_python.egg-info/SOURCES.txt +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif_python.egg-info/dependency_links.txt +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif_python.egg-info/requires.txt +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/onvif_python.egg-info/top_level.txt +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/setup.cfg +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/setup.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/tests/test_client.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/tests/test_core.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/tests/test_exceptions.py +0 -0
- {onvif_python-0.0.4 → onvif_python-0.0.5}/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.5
|
|
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
|
|
@@ -41,9 +41,9 @@ Dynamic: license-file
|
|
|
41
41
|
|
|
42
42
|
[](https://github.com/nirsimetri/onvif-python?tab=MIT-1-ov-file)
|
|
43
43
|
[](https://deepwiki.com/nirsimetri/onvif-python)
|
|
44
|
-
[](https://github.com/nirsimetri/onvif-python/releases)
|
|
45
45
|
<br>
|
|
46
|
-
[](https://pypi.org/project/onvif-python/)
|
|
47
47
|
[](https://clickpy.clickhouse.com/dashboard/onvif-python)
|
|
48
48
|
|
|
49
49
|
Are you having trouble finding a Python ONVIF library that supports your device?
|
|
@@ -148,32 +148,64 @@ Explore more advanced usage and service-specific operations in the [`examples/`]
|
|
|
148
148
|
> [!IMPORTANT]
|
|
149
149
|
> 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.
|
|
150
150
|
|
|
151
|
-
##
|
|
151
|
+
## Service Discovery: Understanding Device Capabilities
|
|
152
152
|
|
|
153
153
|
> [!WARNING]
|
|
154
|
-
> Before performing any operations on an ONVIF device, it is highly recommended to
|
|
154
|
+
> 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 uses `GetServices` during initialization to discover service endpoints, but you can also query services manually for detailed information including capabilities.
|
|
155
155
|
|
|
156
|
-
**Why
|
|
156
|
+
**Why discover device services?**
|
|
157
157
|
|
|
158
|
-
- **Device Diversity:** Not all ONVIF devices support every
|
|
159
|
-
- **Error Prevention:** Attempting to use unsupported
|
|
160
|
-
- **Dynamic Feature Detection:** Devices may enable or disable
|
|
161
|
-
- **Optimized Integration:** By checking available
|
|
158
|
+
- **Device Diversity:** Not all ONVIF devices support every service. Available services may vary by manufacturer, model, firmware, or configuration.
|
|
159
|
+
- **Error Prevention:** Attempting to use unsupported services can result in failed requests, exceptions, or undefined behavior.
|
|
160
|
+
- **Dynamic Feature Detection:** Devices may enable or disable services over time (e.g., after firmware updates or configuration changes).
|
|
161
|
+
- **Optimized Integration:** By checking available services, your application can adapt its workflow and UI to match the device's actual features.
|
|
162
162
|
|
|
163
|
-
**How
|
|
163
|
+
**How service discovery works in this library:**
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
The `ONVIFClient` automatically calls `GetServices` during initialization to build a service map. This map is used internally to resolve service endpoints:
|
|
166
166
|
|
|
167
167
|
```python
|
|
168
168
|
from onvif import ONVIFClient
|
|
169
169
|
|
|
170
170
|
client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
|
|
171
|
+
|
|
172
|
+
# Access the discovered services
|
|
173
|
+
print(client.services)
|
|
174
|
+
# Example output: [{'Namespace': 'http://www.onvif.org/ver10/device/wsdl', 'XAddr': '...', 'Version': {...}}, ...]
|
|
175
|
+
|
|
176
|
+
# Check the service map (namespace -> XAddr mapping)
|
|
177
|
+
print(client._service_map)
|
|
178
|
+
# Example output: {'http://www.onvif.org/ver10/media/wsdl': 'http://192.168.1.17:8000/onvif/Media', ...}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Get detailed service information with capabilities:**
|
|
182
|
+
|
|
183
|
+
If you need detailed capability information for each service, call `GetServices` with `IncludeCapability=True`:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
device = client.devicemgmt()
|
|
187
|
+
services = device.GetServices(IncludeCapability=True)
|
|
188
|
+
|
|
189
|
+
for service in services:
|
|
190
|
+
print(f"Service: {service.Namespace}")
|
|
191
|
+
print(f"Endpoint: {service.XAddr}")
|
|
192
|
+
print(f"Version: {service.Version.Major}.{service.Version.Minor}")
|
|
193
|
+
if hasattr(service, 'Capabilities') and service.Capabilities:
|
|
194
|
+
print(f"Capabilities: {service.Capabilities}")
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Alternative: Use GetCapabilities for legacy compatibility:**
|
|
198
|
+
|
|
199
|
+
For backward compatibility or when you need a quick overview of major service categories, you can still use `GetCapabilities`:
|
|
200
|
+
|
|
201
|
+
```python
|
|
171
202
|
capabilities = client.devicemgmt().GetCapabilities()
|
|
172
203
|
print(capabilities)
|
|
173
|
-
# Example output: {'Media': {'XAddr': '
|
|
204
|
+
# Example output: {'Media': {'XAddr': '...', ...}, 'PTZ': {...}, 'Events': {...}, ...}
|
|
174
205
|
```
|
|
175
206
|
|
|
176
|
-
|
|
207
|
+
> [!TIP]
|
|
208
|
+
> The library handles service discovery automatically, so you typically don't need to call `GetServices` manually unless you need detailed capability information or want to refresh the service list after device configuration changes.
|
|
177
209
|
|
|
178
210
|
## Tested Devices
|
|
179
211
|
|
|
@@ -283,6 +315,7 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
|
|
|
283
315
|
client.events() # root binding
|
|
284
316
|
client.pullpoint(subscription) # sub-binding (dynamic, via SubscriptionReference.Address)
|
|
285
317
|
client.subscription(subscription) # sub-binding (dynamic, via SubscriptionReference.Address)
|
|
318
|
+
client.notification() # sub-binding accessor
|
|
286
319
|
```
|
|
287
320
|
|
|
288
321
|
2. **Security (Advanced Security)**
|
|
@@ -290,7 +323,6 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
|
|
|
290
323
|
- **Sub-bindings:**
|
|
291
324
|
- `AuthorizationServerBinding`
|
|
292
325
|
- `KeystoreBinding`
|
|
293
|
-
- `CredentialBinding`
|
|
294
326
|
- `JWTBinding`
|
|
295
327
|
- `Dot1XBinding`
|
|
296
328
|
- `TLSServerBinding`
|
|
@@ -300,7 +332,7 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
|
|
|
300
332
|
```python
|
|
301
333
|
client.security() # root binding
|
|
302
334
|
client.authorizationserver(xaddr) # sub-binding accessor (requires xAddr)
|
|
303
|
-
client.keystore(xaddr)
|
|
335
|
+
client.keystore(xaddr) # ..
|
|
304
336
|
client.jwt(xaddr)
|
|
305
337
|
client.dot1x(xaddr)
|
|
306
338
|
client.tlsserver(xaddr)
|
|
@@ -326,6 +358,7 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
|
|
|
326
358
|
|
|
327
359
|
## Future Improvements (Stay tuned and star ⭐ this repo)
|
|
328
360
|
|
|
361
|
+
- [ ] Add ONVIF CLI program to interact directly with ONVIF devices via terminal.
|
|
329
362
|
- [ ] Implement structured data models for ONVIF Schemas using [xsdata](https://github.com/tefra/xsdata).
|
|
330
363
|
- [ ] Integrate [xmltodict](https://github.com/martinblech/xmltodict) for simplified XML parsing and conversion.
|
|
331
364
|
- [ ] Add functionality for `ONVIFClient` to accept a custom `wsdl_path` service.
|
|
@@ -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,32 +109,64 @@ 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
|
-
##
|
|
112
|
+
## Service Discovery: Understanding Device Capabilities
|
|
113
113
|
|
|
114
114
|
> [!WARNING]
|
|
115
|
-
> Before performing any operations on an ONVIF device, it is highly recommended to
|
|
115
|
+
> 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 uses `GetServices` during initialization to discover service endpoints, but you can also query services manually for detailed information including capabilities.
|
|
116
116
|
|
|
117
|
-
**Why
|
|
117
|
+
**Why discover device services?**
|
|
118
118
|
|
|
119
|
-
- **Device Diversity:** Not all ONVIF devices support every
|
|
120
|
-
- **Error Prevention:** Attempting to use unsupported
|
|
121
|
-
- **Dynamic Feature Detection:** Devices may enable or disable
|
|
122
|
-
- **Optimized Integration:** By checking available
|
|
119
|
+
- **Device Diversity:** Not all ONVIF devices support every service. Available services may vary by manufacturer, model, firmware, or configuration.
|
|
120
|
+
- **Error Prevention:** Attempting to use unsupported services can result in failed requests, exceptions, or undefined behavior.
|
|
121
|
+
- **Dynamic Feature Detection:** Devices may enable or disable services over time (e.g., after firmware updates or configuration changes).
|
|
122
|
+
- **Optimized Integration:** By checking available services, your application can adapt its workflow and UI to match the device's actual features.
|
|
123
123
|
|
|
124
|
-
**How
|
|
124
|
+
**How service discovery works in this library:**
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
The `ONVIFClient` automatically calls `GetServices` during initialization to build a service map. This map is used internally to resolve service endpoints:
|
|
127
127
|
|
|
128
128
|
```python
|
|
129
129
|
from onvif import ONVIFClient
|
|
130
130
|
|
|
131
131
|
client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
|
|
132
|
+
|
|
133
|
+
# Access the discovered services
|
|
134
|
+
print(client.services)
|
|
135
|
+
# Example output: [{'Namespace': 'http://www.onvif.org/ver10/device/wsdl', 'XAddr': '...', 'Version': {...}}, ...]
|
|
136
|
+
|
|
137
|
+
# Check the service map (namespace -> XAddr mapping)
|
|
138
|
+
print(client._service_map)
|
|
139
|
+
# Example output: {'http://www.onvif.org/ver10/media/wsdl': 'http://192.168.1.17:8000/onvif/Media', ...}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Get detailed service information with capabilities:**
|
|
143
|
+
|
|
144
|
+
If you need detailed capability information for each service, call `GetServices` with `IncludeCapability=True`:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
device = client.devicemgmt()
|
|
148
|
+
services = device.GetServices(IncludeCapability=True)
|
|
149
|
+
|
|
150
|
+
for service in services:
|
|
151
|
+
print(f"Service: {service.Namespace}")
|
|
152
|
+
print(f"Endpoint: {service.XAddr}")
|
|
153
|
+
print(f"Version: {service.Version.Major}.{service.Version.Minor}")
|
|
154
|
+
if hasattr(service, 'Capabilities') and service.Capabilities:
|
|
155
|
+
print(f"Capabilities: {service.Capabilities}")
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Alternative: Use GetCapabilities for legacy compatibility:**
|
|
159
|
+
|
|
160
|
+
For backward compatibility or when you need a quick overview of major service categories, you can still use `GetCapabilities`:
|
|
161
|
+
|
|
162
|
+
```python
|
|
132
163
|
capabilities = client.devicemgmt().GetCapabilities()
|
|
133
164
|
print(capabilities)
|
|
134
|
-
# Example output: {'Media': {'XAddr': '
|
|
165
|
+
# Example output: {'Media': {'XAddr': '...', ...}, 'PTZ': {...}, 'Events': {...}, ...}
|
|
135
166
|
```
|
|
136
167
|
|
|
137
|
-
|
|
168
|
+
> [!TIP]
|
|
169
|
+
> The library handles service discovery automatically, so you typically don't need to call `GetServices` manually unless you need detailed capability information or want to refresh the service list after device configuration changes.
|
|
138
170
|
|
|
139
171
|
## Tested Devices
|
|
140
172
|
|
|
@@ -244,6 +276,7 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
|
|
|
244
276
|
client.events() # root binding
|
|
245
277
|
client.pullpoint(subscription) # sub-binding (dynamic, via SubscriptionReference.Address)
|
|
246
278
|
client.subscription(subscription) # sub-binding (dynamic, via SubscriptionReference.Address)
|
|
279
|
+
client.notification() # sub-binding accessor
|
|
247
280
|
```
|
|
248
281
|
|
|
249
282
|
2. **Security (Advanced Security)**
|
|
@@ -251,7 +284,6 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
|
|
|
251
284
|
- **Sub-bindings:**
|
|
252
285
|
- `AuthorizationServerBinding`
|
|
253
286
|
- `KeystoreBinding`
|
|
254
|
-
- `CredentialBinding`
|
|
255
287
|
- `JWTBinding`
|
|
256
288
|
- `Dot1XBinding`
|
|
257
289
|
- `TLSServerBinding`
|
|
@@ -261,7 +293,7 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
|
|
|
261
293
|
```python
|
|
262
294
|
client.security() # root binding
|
|
263
295
|
client.authorizationserver(xaddr) # sub-binding accessor (requires xAddr)
|
|
264
|
-
client.keystore(xaddr)
|
|
296
|
+
client.keystore(xaddr) # ..
|
|
265
297
|
client.jwt(xaddr)
|
|
266
298
|
client.dot1x(xaddr)
|
|
267
299
|
client.tlsserver(xaddr)
|
|
@@ -287,6 +319,7 @@ Some ONVIF services have multiple bindings in the same WSDL. These typically inc
|
|
|
287
319
|
|
|
288
320
|
## Future Improvements (Stay tuned and star ⭐ this repo)
|
|
289
321
|
|
|
322
|
+
- [ ] Add ONVIF CLI program to interact directly with ONVIF devices via terminal.
|
|
290
323
|
- [ ] Implement structured data models for ONVIF Schemas using [xsdata](https://github.com/tefra/xsdata).
|
|
291
324
|
- [ ] Integrate [xmltodict](https://github.com/martinblech/xmltodict) for simplified XML parsing and conversion.
|
|
292
325
|
- [ ] Add functionality for `ONVIFClient` to accept a custom `wsdl_path` service.
|
|
@@ -41,7 +41,11 @@ from .services import (
|
|
|
41
41
|
MediaSigning,
|
|
42
42
|
)
|
|
43
43
|
from .operator import CacheMode
|
|
44
|
-
from .utils.zeep import
|
|
44
|
+
from .utils.zeep import (
|
|
45
|
+
apply_patch as _apply_zeep_patch,
|
|
46
|
+
remove_patch as _remove_zeep_patch,
|
|
47
|
+
)
|
|
48
|
+
from .utils.wsdl import ONVIFWSDL
|
|
45
49
|
|
|
46
50
|
|
|
47
51
|
class ONVIFClient:
|
|
@@ -58,13 +62,13 @@ class ONVIFClient:
|
|
|
58
62
|
apply_patch=True,
|
|
59
63
|
):
|
|
60
64
|
self.apply_patch = apply_patch
|
|
61
|
-
|
|
65
|
+
|
|
62
66
|
# Apply or remove zeep patch based on user preference
|
|
63
67
|
if apply_patch:
|
|
64
68
|
_apply_zeep_patch()
|
|
65
69
|
else:
|
|
66
70
|
_remove_zeep_patch()
|
|
67
|
-
|
|
71
|
+
|
|
68
72
|
self.common_args = {
|
|
69
73
|
"host": host,
|
|
70
74
|
"port": port,
|
|
@@ -80,8 +84,15 @@ class ONVIFClient:
|
|
|
80
84
|
# Device Management (Core) service is always available
|
|
81
85
|
self._devicemgmt = Device(**self.common_args)
|
|
82
86
|
|
|
83
|
-
# Retrieve device
|
|
84
|
-
self.
|
|
87
|
+
# Retrieve device services once and create namespace -> XAddr mapping
|
|
88
|
+
self.services = self._devicemgmt.GetServices(IncludeCapability=False)
|
|
89
|
+
self._service_map = {}
|
|
90
|
+
for service in self.services:
|
|
91
|
+
namespace = getattr(service, "Namespace", None)
|
|
92
|
+
xaddr = getattr(service, "XAddr", None)
|
|
93
|
+
|
|
94
|
+
if namespace and xaddr:
|
|
95
|
+
self._service_map[namespace] = xaddr
|
|
85
96
|
|
|
86
97
|
# Lazy init for other services
|
|
87
98
|
|
|
@@ -141,45 +152,49 @@ class ONVIFClient:
|
|
|
141
152
|
self._authorizationserver = None
|
|
142
153
|
self._mediasigning = None
|
|
143
154
|
|
|
144
|
-
|
|
145
155
|
def _get_xaddr(self, service_name: str, service_path: str):
|
|
146
156
|
"""
|
|
147
|
-
Resolve XAddr from
|
|
148
|
-
|
|
157
|
+
Resolve XAddr from GetServices based on namespace mapping.
|
|
158
|
+
Falls back to default URL if service not found.
|
|
149
159
|
"""
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
160
|
+
# Get the namespace for this service from WSDL_MAP
|
|
161
|
+
try:
|
|
162
|
+
# Try to get the service definition from WSDL_MAP
|
|
163
|
+
# Most services use ver10, some use ver20
|
|
164
|
+
service_key = service_name.lower()
|
|
165
|
+
wsdl_def = None
|
|
166
|
+
|
|
167
|
+
# Try ver10 first, then ver20
|
|
168
|
+
if service_key in ONVIFWSDL.WSDL_MAP:
|
|
169
|
+
if "ver10" in ONVIFWSDL.WSDL_MAP[service_key]:
|
|
170
|
+
wsdl_def = ONVIFWSDL.WSDL_MAP[service_key]["ver10"]
|
|
171
|
+
elif "ver20" in ONVIFWSDL.WSDL_MAP[service_key]:
|
|
172
|
+
wsdl_def = ONVIFWSDL.WSDL_MAP[service_key]["ver20"]
|
|
173
|
+
|
|
174
|
+
if wsdl_def:
|
|
175
|
+
namespace = wsdl_def["namespace"]
|
|
176
|
+
xaddr = self._service_map.get(namespace)
|
|
177
|
+
|
|
178
|
+
if xaddr:
|
|
179
|
+
# Rewrite host/port if needed
|
|
180
|
+
parsed = urlparse(xaddr)
|
|
181
|
+
device_host = parsed.hostname
|
|
182
|
+
device_port = parsed.port
|
|
183
|
+
connect_host = self.common_args["host"]
|
|
184
|
+
connect_port = self.common_args["port"]
|
|
185
|
+
|
|
186
|
+
if (device_host != connect_host) or (device_port != connect_port):
|
|
187
|
+
protocol = "https" if self.common_args["use_https"] else "http"
|
|
188
|
+
new_netloc = f"{connect_host}:{connect_port}"
|
|
189
|
+
rewritten = urlunparse(
|
|
190
|
+
(protocol, new_netloc, parsed.path, "", "", "")
|
|
191
|
+
)
|
|
192
|
+
return rewritten
|
|
193
|
+
return xaddr
|
|
194
|
+
except Exception:
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
# Fallback to default URL
|
|
183
198
|
protocol = "https" if self.common_args["use_https"] else "http"
|
|
184
199
|
return f"{protocol}://{self.common_args['host']}:{self.common_args['port']}/onvif/{service_path}"
|
|
185
200
|
|
|
@@ -125,7 +125,7 @@ class ONVIFOperator:
|
|
|
125
125
|
|
|
126
126
|
try:
|
|
127
127
|
result = func(*args, **kwargs)
|
|
128
|
-
# Post-process to flatten xsd:any fields if enabled
|
|
128
|
+
# Post-process to flatten xsd:any fields if enabled (> v0.0.4 patch)
|
|
129
129
|
if self.apply_flatten:
|
|
130
130
|
return flatten_xsd_any_fields(result)
|
|
131
131
|
return result
|
|
@@ -25,7 +25,7 @@ class Device:
|
|
|
25
25
|
return self.operator.call("GetDeviceInformation")
|
|
26
26
|
|
|
27
27
|
def SetSystemDateAndTime(
|
|
28
|
-
self, DateTimeType, DaylightSavings
|
|
28
|
+
self, DateTimeType, DaylightSavings, TimeZone=None, UTCDateTime=None
|
|
29
29
|
):
|
|
30
30
|
return self.operator.call(
|
|
31
31
|
"SetSystemDateAndTime",
|