onvif-python 0.0.1__py3-none-any.whl
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/__init__.py +13 -0
- onvif/client.py +485 -0
- onvif/operator.py +130 -0
- onvif/services/__init__.py +75 -0
- onvif/services/accesscontrol.py +105 -0
- onvif/services/accessrules.py +47 -0
- onvif/services/actionengine.py +46 -0
- onvif/services/analytics/analytics.py +60 -0
- onvif/services/analytics/ruleengine.py +44 -0
- onvif/services/analyticsdevice.py +122 -0
- onvif/services/appmgmt.py +40 -0
- onvif/services/authenticationbehavior.py +83 -0
- onvif/services/credential.py +159 -0
- onvif/services/deviceio.py +165 -0
- onvif/services/devicemgmt.py +410 -0
- onvif/services/display.py +62 -0
- onvif/services/doorcontrol.py +93 -0
- onvif/services/events/events.py +41 -0
- onvif/services/events/notification.py +34 -0
- onvif/services/events/pullpoint.py +29 -0
- onvif/services/events/subscription.py +21 -0
- onvif/services/imaging.py +62 -0
- onvif/services/media.py +424 -0
- onvif/services/media2.py +310 -0
- onvif/services/provisioning.py +50 -0
- onvif/services/ptz.py +187 -0
- onvif/services/receiver.py +46 -0
- onvif/services/recording.py +136 -0
- onvif/services/replay.py +30 -0
- onvif/services/schedule.py +82 -0
- onvif/services/search.py +149 -0
- onvif/services/security/advancedsecurity.py +19 -0
- onvif/services/security/authorizationserver.py +31 -0
- onvif/services/security/dot1x.py +42 -0
- onvif/services/security/jwt.py +21 -0
- onvif/services/security/keystore.py +208 -0
- onvif/services/security/mediasigning.py +30 -0
- onvif/services/security/tlsserver.py +82 -0
- onvif/services/thermal.py +54 -0
- onvif/services/uplink.py +28 -0
- onvif/utils/__init__.py +4 -0
- onvif/utils/exceptions.py +33 -0
- onvif/utils/wsdl.py +297 -0
- onvif/wsdl/ver10/accesscontrol/wsdl/index.html +7 -0
- onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +702 -0
- onvif/wsdl/ver10/accessrules/wsdl/index.html +7 -0
- onvif/wsdl/ver10/actionengine.wsdl +1265 -0
- onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +4159 -0
- onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +7 -0
- onvif/wsdl/ver10/analyticsdevice.wsdl +709 -0
- onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +502 -0
- onvif/wsdl/ver10/appmgmt/wsdl/index.html +7 -0
- onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +1382 -0
- onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +7 -0
- onvif/wsdl/ver10/credential/wsdl/credential.wsdl +2018 -0
- onvif/wsdl/ver10/credential/wsdl/index.html +7 -0
- onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +4623 -0
- onvif/wsdl/ver10/deviceio.wsdl +1414 -0
- onvif/wsdl/ver10/display/display.wsdl +383 -0
- onvif/wsdl/ver10/display/wsdl/index.htm +383 -0
- onvif/wsdl/ver10/display.wsdl +521 -0
- onvif/wsdl/ver10/doorcontrol/wsdl/index.html +7 -0
- onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +449 -0
- onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +947 -0
- onvif/wsdl/ver10/events/wsdl/event.wsdl +947 -0
- onvif/wsdl/ver10/events/wsdl/index.html +7 -0
- onvif/wsdl/ver10/federatedsearch.wsdl +344 -0
- onvif/wsdl/ver10/media/wsdl/media.wsdl +3684 -0
- onvif/wsdl/ver10/pacs/accesscontrol.wsdl +1942 -0
- onvif/wsdl/ver10/pacs/doorcontrol.wsdl +1792 -0
- onvif/wsdl/ver10/pacs/types.xsd +181 -0
- onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +588 -0
- onvif/wsdl/ver10/receiver.wsdl +398 -0
- onvif/wsdl/ver10/recording.wsdl +1231 -0
- onvif/wsdl/ver10/replay.wsdl +221 -0
- onvif/wsdl/ver10/schedule/wsdl/index.html +7 -0
- onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +1383 -0
- onvif/wsdl/ver10/schema/common.xsd +422 -0
- onvif/wsdl/ver10/schema/metadatastream.xsd +520 -0
- onvif/wsdl/ver10/schema/onvif.xsd +9251 -0
- onvif/wsdl/ver10/search.wsdl +882 -0
- onvif/wsdl/ver10/thermal/wsdl/index.html +7 -0
- onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +777 -0
- onvif/wsdl/ver10/topics/topicns.xml +13 -0
- onvif/wsdl/ver10/uplink/wsdl/index.html +7 -0
- onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +256 -0
- onvif/wsdl/ver20/analytics/humanbody.xsd +490 -0
- onvif/wsdl/ver20/analytics/humanface.xsd +501 -0
- onvif/wsdl/ver20/analytics/index.html +7 -0
- onvif/wsdl/ver20/analytics/radiometry.xsd +389 -0
- onvif/wsdl/ver20/analytics/rules.xsd +127 -0
- onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +757 -0
- onvif/wsdl/ver20/analytics/wsdl/index.html +7 -0
- onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +603 -0
- onvif/wsdl/ver20/media/wsdl/index.html +7 -0
- onvif/wsdl/ver20/media/wsdl/media.wsdl +2862 -0
- onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +1450 -0
- onvif/wsdl/ver20/util/federated_search_documentation.html +1647 -0
- onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +8 -0
- onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +2580 -0
- onvif/wsdl/ver20/util/operationIndex.html +97 -0
- onvif_python-0.0.1.dist-info/METADATA +381 -0
- onvif_python-0.0.1.dist-info/RECORD +106 -0
- onvif_python-0.0.1.dist-info/WHEEL +5 -0
- onvif_python-0.0.1.dist-info/licenses/LICENSE.md +21 -0
- onvif_python-0.0.1.dist-info/top_level.txt +1 -0
onvif/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# onvif/__init__.py
|
|
2
|
+
|
|
3
|
+
from .client import ONVIFClient
|
|
4
|
+
from .operator import ONVIFOperator, CacheMode
|
|
5
|
+
from .utils import ONVIFWSDL, ONVIFOperationException
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"ONVIFClient",
|
|
9
|
+
"ONVIFOperator",
|
|
10
|
+
"CacheMode",
|
|
11
|
+
"ONVIFWSDL",
|
|
12
|
+
"ONVIFOperationException",
|
|
13
|
+
]
|
onvif/client.py
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
# onvif/client.py
|
|
2
|
+
|
|
3
|
+
from urllib.parse import urlparse, urlunparse
|
|
4
|
+
|
|
5
|
+
from .services import (
|
|
6
|
+
Device,
|
|
7
|
+
Events,
|
|
8
|
+
PullPoint,
|
|
9
|
+
Notification,
|
|
10
|
+
Subscription,
|
|
11
|
+
Imaging,
|
|
12
|
+
Media,
|
|
13
|
+
Media2,
|
|
14
|
+
PTZ,
|
|
15
|
+
DeviceIO,
|
|
16
|
+
AccessControl,
|
|
17
|
+
AccessRules,
|
|
18
|
+
ActionEngine,
|
|
19
|
+
Analytics,
|
|
20
|
+
RuleEngine,
|
|
21
|
+
AnalyticsDevice,
|
|
22
|
+
AppManagement,
|
|
23
|
+
AuthenticationBehavior,
|
|
24
|
+
Credential,
|
|
25
|
+
Recording,
|
|
26
|
+
Replay,
|
|
27
|
+
Display,
|
|
28
|
+
DoorControl,
|
|
29
|
+
Provisioning,
|
|
30
|
+
Receiver,
|
|
31
|
+
Schedule,
|
|
32
|
+
Search,
|
|
33
|
+
Thermal,
|
|
34
|
+
Uplink,
|
|
35
|
+
AdvancedSecurity,
|
|
36
|
+
JWT,
|
|
37
|
+
Keystore,
|
|
38
|
+
TLSServer,
|
|
39
|
+
Dot1X,
|
|
40
|
+
AuthorizationServer,
|
|
41
|
+
MediaSigning,
|
|
42
|
+
)
|
|
43
|
+
from .operator import CacheMode
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ONVIFClient:
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
host,
|
|
50
|
+
port,
|
|
51
|
+
username,
|
|
52
|
+
password,
|
|
53
|
+
timeout=10,
|
|
54
|
+
cache=CacheMode.ALL,
|
|
55
|
+
use_https=False,
|
|
56
|
+
verify_ssl=True,
|
|
57
|
+
):
|
|
58
|
+
self.common_args = {
|
|
59
|
+
"host": host,
|
|
60
|
+
"port": port,
|
|
61
|
+
"username": username,
|
|
62
|
+
"password": password,
|
|
63
|
+
"timeout": timeout,
|
|
64
|
+
"cache": cache,
|
|
65
|
+
"use_https": use_https,
|
|
66
|
+
"verify_ssl": verify_ssl,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Device Management (Core) service is always available
|
|
70
|
+
self._devicemgmt = Device(**self.common_args)
|
|
71
|
+
|
|
72
|
+
# Retrieve device capabilities once and reuse
|
|
73
|
+
self.capabilities = self._devicemgmt.GetCapabilities(Category="All")
|
|
74
|
+
|
|
75
|
+
# Lazy init for other services
|
|
76
|
+
|
|
77
|
+
self._events = None
|
|
78
|
+
self._pullpoint = None
|
|
79
|
+
self._notification = None
|
|
80
|
+
self._subscription = None
|
|
81
|
+
|
|
82
|
+
self._imaging = None
|
|
83
|
+
|
|
84
|
+
self._media = None
|
|
85
|
+
self._media2 = None
|
|
86
|
+
|
|
87
|
+
self._ptz = None
|
|
88
|
+
|
|
89
|
+
self._deviceio = None
|
|
90
|
+
|
|
91
|
+
self._display = None
|
|
92
|
+
|
|
93
|
+
self._analytics = None
|
|
94
|
+
self._ruleengine = None
|
|
95
|
+
self._analyticsdevice = None
|
|
96
|
+
|
|
97
|
+
self._accesscontrol = None
|
|
98
|
+
self._doorcontrol = None
|
|
99
|
+
|
|
100
|
+
self._accessrules = None
|
|
101
|
+
|
|
102
|
+
self._actionengine = None
|
|
103
|
+
|
|
104
|
+
self._appmanagement = None
|
|
105
|
+
|
|
106
|
+
self._authenticationbehavior = None
|
|
107
|
+
|
|
108
|
+
self._credential = None
|
|
109
|
+
|
|
110
|
+
self._recording = None
|
|
111
|
+
self._replay = None
|
|
112
|
+
|
|
113
|
+
self._provisioning = None
|
|
114
|
+
|
|
115
|
+
self._receiver = None
|
|
116
|
+
|
|
117
|
+
self._schedule = None
|
|
118
|
+
|
|
119
|
+
self._search = None
|
|
120
|
+
|
|
121
|
+
self._thermal = None
|
|
122
|
+
|
|
123
|
+
self._uplink = None
|
|
124
|
+
|
|
125
|
+
self._security = None
|
|
126
|
+
self._jwt = None
|
|
127
|
+
self._keystore = None
|
|
128
|
+
self._tlsserver = None
|
|
129
|
+
self._dot1x = None
|
|
130
|
+
self._authorizationserver = None
|
|
131
|
+
self._mediasigning = None
|
|
132
|
+
|
|
133
|
+
def _get_xaddr(self, service_name: str, service_path: str):
|
|
134
|
+
"""
|
|
135
|
+
Resolve XAddr from GetCapabilities. Fallback to default if not present.
|
|
136
|
+
"""
|
|
137
|
+
svc = getattr(self.capabilities, service_name, None)
|
|
138
|
+
if svc and hasattr(svc, "XAddr") and svc.XAddr:
|
|
139
|
+
parsed = urlparse(svc.XAddr)
|
|
140
|
+
|
|
141
|
+
# Host/port from device
|
|
142
|
+
device_host = parsed.hostname
|
|
143
|
+
device_port = parsed.port
|
|
144
|
+
|
|
145
|
+
# Host/port from connection
|
|
146
|
+
connect_host = self.common_args["host"]
|
|
147
|
+
connect_port = self.common_args["port"]
|
|
148
|
+
|
|
149
|
+
# if host/port differ, rewrite XAddr to use connection values
|
|
150
|
+
if (device_host != connect_host) or (device_port != connect_port):
|
|
151
|
+
protocol = parsed.scheme
|
|
152
|
+
new_netloc = f"{connect_host}:{connect_port}"
|
|
153
|
+
rewritten = urlunparse((protocol, new_netloc, parsed.path, "", "", ""))
|
|
154
|
+
return rewritten
|
|
155
|
+
|
|
156
|
+
return svc.XAddr
|
|
157
|
+
|
|
158
|
+
# fallback (legacy style)
|
|
159
|
+
protocol = "https" if self.common_args["use_https"] else "http"
|
|
160
|
+
return f"{protocol}://{self.common_args['host']}:{self.common_args['port']}/onvif/{service_path}"
|
|
161
|
+
|
|
162
|
+
# Core (Device Management)
|
|
163
|
+
|
|
164
|
+
def devicemgmt(self):
|
|
165
|
+
return self._devicemgmt
|
|
166
|
+
|
|
167
|
+
# Core (Events)
|
|
168
|
+
|
|
169
|
+
def events(self):
|
|
170
|
+
if self._events is None:
|
|
171
|
+
self._events = Events(
|
|
172
|
+
xaddr=self._get_xaddr("Events", "Events"), **self.common_args
|
|
173
|
+
)
|
|
174
|
+
return self._events
|
|
175
|
+
|
|
176
|
+
def pullpoint(self, SubscriptionRef):
|
|
177
|
+
if self._pullpoint is None:
|
|
178
|
+
xaddr = None
|
|
179
|
+
try:
|
|
180
|
+
addr_obj = SubscriptionRef["SubscriptionReference"]["Address"]
|
|
181
|
+
if isinstance(addr_obj, dict) and "_value_1" in addr_obj:
|
|
182
|
+
xaddr = addr_obj["_value_1"]
|
|
183
|
+
elif hasattr(addr_obj, "_value_1"):
|
|
184
|
+
xaddr = addr_obj._value_1
|
|
185
|
+
except Exception:
|
|
186
|
+
pass
|
|
187
|
+
|
|
188
|
+
if not xaddr:
|
|
189
|
+
raise RuntimeError(
|
|
190
|
+
"SubscriptionReference.Address missing in subscription response"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
self._pullpoint = PullPoint(xaddr=xaddr, **self.common_args)
|
|
194
|
+
return self._pullpoint
|
|
195
|
+
|
|
196
|
+
def notification(self):
|
|
197
|
+
if self._notification is None:
|
|
198
|
+
self._notification = Notification(
|
|
199
|
+
xaddr=self._get_xaddr("Events", "Events"), **self.common_args
|
|
200
|
+
)
|
|
201
|
+
return self._notification
|
|
202
|
+
|
|
203
|
+
def subscription(self, SubscriptionRef):
|
|
204
|
+
if self._subscription is None:
|
|
205
|
+
xaddr = None
|
|
206
|
+
try:
|
|
207
|
+
addr_obj = SubscriptionRef["SubscriptionReference"]["Address"]
|
|
208
|
+
if isinstance(addr_obj, dict) and "_value_1" in addr_obj:
|
|
209
|
+
xaddr = addr_obj["_value_1"]
|
|
210
|
+
elif hasattr(addr_obj, "_value_1"):
|
|
211
|
+
xaddr = addr_obj._value_1
|
|
212
|
+
except Exception:
|
|
213
|
+
pass
|
|
214
|
+
|
|
215
|
+
if not xaddr:
|
|
216
|
+
raise RuntimeError(
|
|
217
|
+
"SubscriptionReference.Address missing in subscription response"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
self._subscription = Subscription(xaddr=xaddr, **self.common_args)
|
|
221
|
+
return self._subscription
|
|
222
|
+
|
|
223
|
+
# Imaging
|
|
224
|
+
|
|
225
|
+
def imaging(self):
|
|
226
|
+
if self._imaging is None:
|
|
227
|
+
self._imaging = Imaging(
|
|
228
|
+
xaddr=self._get_xaddr("Imaging", "Imaging"), **self.common_args
|
|
229
|
+
)
|
|
230
|
+
return self._imaging
|
|
231
|
+
|
|
232
|
+
# Media
|
|
233
|
+
|
|
234
|
+
def media(self):
|
|
235
|
+
if self._media is None:
|
|
236
|
+
self._media = Media(
|
|
237
|
+
xaddr=self._get_xaddr("Media", "Media"), **self.common_args
|
|
238
|
+
)
|
|
239
|
+
return self._media
|
|
240
|
+
|
|
241
|
+
def media2(self):
|
|
242
|
+
if self._media2 is None:
|
|
243
|
+
self._media2 = Media2(
|
|
244
|
+
xaddr=self._get_xaddr("Media2", "Media2"), **self.common_args
|
|
245
|
+
)
|
|
246
|
+
return self._media2
|
|
247
|
+
|
|
248
|
+
# PTZ
|
|
249
|
+
|
|
250
|
+
def ptz(self):
|
|
251
|
+
if self._ptz is None:
|
|
252
|
+
self._ptz = PTZ(xaddr=self._get_xaddr("PTZ", "PTZ"), **self.common_args)
|
|
253
|
+
return self._ptz
|
|
254
|
+
|
|
255
|
+
# DeviceIO
|
|
256
|
+
|
|
257
|
+
def deviceio(self):
|
|
258
|
+
if self._deviceio is None:
|
|
259
|
+
self._deviceio = DeviceIO(
|
|
260
|
+
xaddr=self._get_xaddr("DeviceIO", "DeviceIO"), **self.common_args
|
|
261
|
+
)
|
|
262
|
+
return self._deviceio
|
|
263
|
+
|
|
264
|
+
# Display
|
|
265
|
+
|
|
266
|
+
def display(self):
|
|
267
|
+
if self._display is None:
|
|
268
|
+
self._display = Display(
|
|
269
|
+
xaddr=self._get_xaddr("Display", "Display"), **self.common_args
|
|
270
|
+
)
|
|
271
|
+
return self._display
|
|
272
|
+
|
|
273
|
+
# Analytics
|
|
274
|
+
|
|
275
|
+
def analytics(self):
|
|
276
|
+
if self._analytics is None:
|
|
277
|
+
self._analytics = Analytics(
|
|
278
|
+
xaddr=self._get_xaddr("Analytics", "Analytics"), **self.common_args
|
|
279
|
+
)
|
|
280
|
+
return self._analytics
|
|
281
|
+
|
|
282
|
+
def ruleengine(self):
|
|
283
|
+
if self._ruleengine is None:
|
|
284
|
+
self._ruleengine = RuleEngine(
|
|
285
|
+
xaddr=self._get_xaddr("Analytics", "Analytics"), **self.common_args
|
|
286
|
+
)
|
|
287
|
+
return self._ruleengine
|
|
288
|
+
|
|
289
|
+
def analyticsdevice(self):
|
|
290
|
+
if self._analyticsdevice is None:
|
|
291
|
+
self._analyticsdevice = AnalyticsDevice(
|
|
292
|
+
xaddr=self._get_xaddr("AnalyticsDevice", "AnalyticsDevice"),
|
|
293
|
+
**self.common_args,
|
|
294
|
+
)
|
|
295
|
+
return self._analyticsdevice
|
|
296
|
+
|
|
297
|
+
# PACS
|
|
298
|
+
|
|
299
|
+
def accesscontrol(self):
|
|
300
|
+
if self._accesscontrol is None:
|
|
301
|
+
self._accesscontrol = AccessControl(
|
|
302
|
+
xaddr=self._get_xaddr("AccessControl", "AccessControl"),
|
|
303
|
+
**self.common_args,
|
|
304
|
+
)
|
|
305
|
+
return self._accesscontrol
|
|
306
|
+
|
|
307
|
+
def doorcontrol(self):
|
|
308
|
+
if self._doorcontrol is None:
|
|
309
|
+
self._doorcontrol = DoorControl(
|
|
310
|
+
xaddr=self._get_xaddr("DoorControl", "DoorControl"), **self.common_args
|
|
311
|
+
)
|
|
312
|
+
return self._doorcontrol
|
|
313
|
+
|
|
314
|
+
# AccessRules
|
|
315
|
+
|
|
316
|
+
def accessrules(self):
|
|
317
|
+
if self._accessrules is None:
|
|
318
|
+
self._accessrules = AccessRules(
|
|
319
|
+
xaddr=self._get_xaddr("AccessRules", "AccessRules"), **self.common_args
|
|
320
|
+
)
|
|
321
|
+
return self._accessrules
|
|
322
|
+
|
|
323
|
+
# ActionEngine
|
|
324
|
+
|
|
325
|
+
def actionengine(self):
|
|
326
|
+
if self._actionengine is None:
|
|
327
|
+
self._actionengine = ActionEngine(
|
|
328
|
+
xaddr=self._get_xaddr("ActionEngine", "ActionEngine"),
|
|
329
|
+
**self.common_args,
|
|
330
|
+
)
|
|
331
|
+
return self._actionengine
|
|
332
|
+
|
|
333
|
+
# AppManagement
|
|
334
|
+
|
|
335
|
+
def appmanagement(self):
|
|
336
|
+
if self._appmanagement is None:
|
|
337
|
+
self._appmanagement = AppManagement(
|
|
338
|
+
xaddr=self._get_xaddr("AppManagement", "AppManagement"),
|
|
339
|
+
**self.common_args,
|
|
340
|
+
)
|
|
341
|
+
return self._appmanagement
|
|
342
|
+
|
|
343
|
+
# AuthenticationBehavior
|
|
344
|
+
|
|
345
|
+
def authenticationbehavior(self):
|
|
346
|
+
if self._authenticationbehavior is None:
|
|
347
|
+
self._authenticationbehavior = AuthenticationBehavior(
|
|
348
|
+
xaddr=self._get_xaddr(
|
|
349
|
+
"AuthenticationBehavior", "AuthenticationBehavior"
|
|
350
|
+
),
|
|
351
|
+
**self.common_args,
|
|
352
|
+
)
|
|
353
|
+
return self._authenticationbehavior
|
|
354
|
+
|
|
355
|
+
# Credential
|
|
356
|
+
|
|
357
|
+
def credential(self):
|
|
358
|
+
if self._credential is None:
|
|
359
|
+
self._credential = Credential(
|
|
360
|
+
xaddr=self._get_xaddr("Credential", "Credential"),
|
|
361
|
+
**self.common_args,
|
|
362
|
+
)
|
|
363
|
+
return self._credential
|
|
364
|
+
|
|
365
|
+
# Recording
|
|
366
|
+
|
|
367
|
+
def recording(self):
|
|
368
|
+
if self._recording is None:
|
|
369
|
+
self._recording = Recording(
|
|
370
|
+
xaddr=self._get_xaddr("Recording", "Recording"),
|
|
371
|
+
**self.common_args,
|
|
372
|
+
)
|
|
373
|
+
return self._recording
|
|
374
|
+
|
|
375
|
+
# Replay
|
|
376
|
+
|
|
377
|
+
def replay(self):
|
|
378
|
+
if self._replay is None:
|
|
379
|
+
self._replay = Replay(
|
|
380
|
+
xaddr=self._get_xaddr("Replay", "Replay"),
|
|
381
|
+
**self.common_args,
|
|
382
|
+
)
|
|
383
|
+
return self._replay
|
|
384
|
+
|
|
385
|
+
# Provisioning
|
|
386
|
+
|
|
387
|
+
def provisioning(self):
|
|
388
|
+
if self._provisioning is None:
|
|
389
|
+
self._provisioning = Provisioning(
|
|
390
|
+
xaddr=self._get_xaddr("Provisioning", "Provisioning"),
|
|
391
|
+
**self.common_args,
|
|
392
|
+
)
|
|
393
|
+
return self._provisioning
|
|
394
|
+
|
|
395
|
+
# Receiver
|
|
396
|
+
|
|
397
|
+
def receiver(self):
|
|
398
|
+
if self._receiver is None:
|
|
399
|
+
self._receiver = Receiver(
|
|
400
|
+
xaddr=self._get_xaddr("Receiver", "Receiver"),
|
|
401
|
+
**self.common_args,
|
|
402
|
+
)
|
|
403
|
+
return self._receiver
|
|
404
|
+
|
|
405
|
+
# Schedule
|
|
406
|
+
|
|
407
|
+
def schedule(self):
|
|
408
|
+
if self._schedule is None:
|
|
409
|
+
self._schedule = Schedule(
|
|
410
|
+
xaddr=self._get_xaddr("Schedule", "Schedule"),
|
|
411
|
+
**self.common_args,
|
|
412
|
+
)
|
|
413
|
+
return self._schedule
|
|
414
|
+
|
|
415
|
+
# Search
|
|
416
|
+
|
|
417
|
+
def search(self):
|
|
418
|
+
if self._search is None:
|
|
419
|
+
self._search = Search(
|
|
420
|
+
xaddr=self._get_xaddr("Search", "Search"),
|
|
421
|
+
**self.common_args,
|
|
422
|
+
)
|
|
423
|
+
return self._search
|
|
424
|
+
|
|
425
|
+
# Thermal
|
|
426
|
+
|
|
427
|
+
def thermal(self):
|
|
428
|
+
if self._thermal is None:
|
|
429
|
+
self._thermal = Thermal(
|
|
430
|
+
xaddr=self._get_xaddr("Thermal", "Thermal"),
|
|
431
|
+
**self.common_args,
|
|
432
|
+
)
|
|
433
|
+
return self._thermal
|
|
434
|
+
|
|
435
|
+
# Uplink
|
|
436
|
+
|
|
437
|
+
def uplink(self):
|
|
438
|
+
if self._uplink is None:
|
|
439
|
+
self._uplink = Uplink(
|
|
440
|
+
xaddr=self._get_xaddr("Uplink", "Uplink"),
|
|
441
|
+
**self.common_args,
|
|
442
|
+
)
|
|
443
|
+
return self._uplink
|
|
444
|
+
|
|
445
|
+
# Security - AdvancedSecurity
|
|
446
|
+
|
|
447
|
+
def security(self):
|
|
448
|
+
if self._security is None:
|
|
449
|
+
self._security = AdvancedSecurity(
|
|
450
|
+
xaddr=self._get_xaddr("Security", "Security"),
|
|
451
|
+
**self.common_args,
|
|
452
|
+
)
|
|
453
|
+
return self._security
|
|
454
|
+
|
|
455
|
+
def jwt(self, xaddr):
|
|
456
|
+
if self._jwt is None:
|
|
457
|
+
self._jwt = JWT(xaddr=xaddr, **self.common_args)
|
|
458
|
+
return self._jwt
|
|
459
|
+
|
|
460
|
+
def keystore(self, xaddr):
|
|
461
|
+
if self._keystore is None:
|
|
462
|
+
self._keystore = Keystore(xaddr=xaddr, **self.common_args)
|
|
463
|
+
return self._keystore
|
|
464
|
+
|
|
465
|
+
def tlsserver(self, xaddr):
|
|
466
|
+
if self._tlsserver is None:
|
|
467
|
+
self._tlsserver = TLSServer(xaddr=xaddr, **self.common_args)
|
|
468
|
+
return self._tlsserver
|
|
469
|
+
|
|
470
|
+
def dot1x(self, xaddr):
|
|
471
|
+
if self._dot1x is None:
|
|
472
|
+
self._dot1x = Dot1X(xaddr=xaddr, **self.common_args)
|
|
473
|
+
return self._dot1x
|
|
474
|
+
|
|
475
|
+
def authorizationserver(self, xaddr):
|
|
476
|
+
if self._authorizationserver is None:
|
|
477
|
+
self._authorizationserver = AuthorizationServer(
|
|
478
|
+
xaddr=xaddr, **self.common_args
|
|
479
|
+
)
|
|
480
|
+
return self._authorizationserver
|
|
481
|
+
|
|
482
|
+
def mediasigning(self, xaddr):
|
|
483
|
+
if self._mediasigning is None:
|
|
484
|
+
self._mediasigning = MediaSigning(xaddr=xaddr, **self.common_args)
|
|
485
|
+
return self._mediasigning
|
onvif/operator.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# onvif/operator.py
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import logging
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from zeep import Settings, Transport, Client, CachingClient
|
|
9
|
+
from zeep.cache import SqliteCache
|
|
10
|
+
from zeep.exceptions import Fault
|
|
11
|
+
from zeep.wsse.username import UsernameToken
|
|
12
|
+
|
|
13
|
+
from .utils import ONVIFOperationException
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CacheMode(Enum):
|
|
17
|
+
ALL = "all" # CachingClient + SqliteCache →
|
|
18
|
+
# (+) Fast startup (WSDL/schema cached in memory + disk), great for multi-device and long-running apps
|
|
19
|
+
# (-) More complex, extra overhead on both disk and memory
|
|
20
|
+
# Use case: Production servers with many cameras, need stability & bandwidth savings
|
|
21
|
+
|
|
22
|
+
DB = "db" # Client + SqliteCache →
|
|
23
|
+
# (+) Persistent disk cache, saves bandwidth (WSDL/schema not fetched every time)
|
|
24
|
+
# (-) Still parses full WSDL into memory at each startup
|
|
25
|
+
# Use case: Batch jobs / CLI tools, or low-resource environments needing long-term cache
|
|
26
|
+
|
|
27
|
+
MEM = "mem" # CachingClient only →
|
|
28
|
+
# (+) Lightweight compared to ALL, in-memory cache only, fast during runtime
|
|
29
|
+
# (-) Cache lost on restart, WSDL will be fetched again after each restart
|
|
30
|
+
# Use case: Short-lived scripts, demos, quick debugging, no need for disk persistence
|
|
31
|
+
|
|
32
|
+
NONE = "none" # Client only →
|
|
33
|
+
# (+) Simplest, no caching at all
|
|
34
|
+
# (-) Slow (always fetches & parses WSDL), high bandwidth usage
|
|
35
|
+
# Use case: Pure debugging, small integration testing without performance concerns
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ONVIFOperator:
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
wsdl_path: str,
|
|
42
|
+
host: str,
|
|
43
|
+
port: int,
|
|
44
|
+
username: str = None,
|
|
45
|
+
password: str = None,
|
|
46
|
+
timeout: int = 10,
|
|
47
|
+
binding: str = None,
|
|
48
|
+
service_path: str = None,
|
|
49
|
+
xaddr: str = None,
|
|
50
|
+
cache: CacheMode = CacheMode.ALL, # all | db | mem | none
|
|
51
|
+
cache_path: str = None,
|
|
52
|
+
use_https: bool = False,
|
|
53
|
+
verify_ssl: bool = True,
|
|
54
|
+
):
|
|
55
|
+
self.wsdl_path = wsdl_path
|
|
56
|
+
self.host = host
|
|
57
|
+
self.port = port
|
|
58
|
+
self.username = username
|
|
59
|
+
self.password = password
|
|
60
|
+
self.timeout = timeout
|
|
61
|
+
|
|
62
|
+
if xaddr:
|
|
63
|
+
self.address = xaddr
|
|
64
|
+
else:
|
|
65
|
+
protocol = "https" if use_https else "http"
|
|
66
|
+
path = service_path or "device_service" # default fallback
|
|
67
|
+
self.address = f"{protocol}://{host}:{port}/onvif/{path}"
|
|
68
|
+
|
|
69
|
+
# Session reuse with retry strategy
|
|
70
|
+
session = requests.Session()
|
|
71
|
+
session.verify = verify_ssl
|
|
72
|
+
|
|
73
|
+
transport_kwargs = {"session": session, "timeout": timeout}
|
|
74
|
+
|
|
75
|
+
if cache in (CacheMode.DB, CacheMode.ALL):
|
|
76
|
+
if cache_path is None:
|
|
77
|
+
user_cache_dir = os.path.expanduser("~/.onvif-python")
|
|
78
|
+
os.makedirs(user_cache_dir, exist_ok=True)
|
|
79
|
+
cache_path = os.path.join(user_cache_dir, "onvif_zeep_cache.sqlite")
|
|
80
|
+
|
|
81
|
+
transport_kwargs["cache"] = SqliteCache(path=cache_path)
|
|
82
|
+
|
|
83
|
+
transport = Transport(**transport_kwargs)
|
|
84
|
+
|
|
85
|
+
# zeep settings
|
|
86
|
+
settings = Settings(strict=False, xml_huge_tree=True)
|
|
87
|
+
wsse = (
|
|
88
|
+
UsernameToken(username, password, use_digest=True)
|
|
89
|
+
if username and password
|
|
90
|
+
else None
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
if cache == CacheMode.ALL:
|
|
94
|
+
ClientType = CachingClient
|
|
95
|
+
elif cache == CacheMode.MEM:
|
|
96
|
+
ClientType = CachingClient
|
|
97
|
+
elif cache == CacheMode.DB:
|
|
98
|
+
ClientType = Client
|
|
99
|
+
elif cache == CacheMode.NONE:
|
|
100
|
+
ClientType = Client
|
|
101
|
+
else:
|
|
102
|
+
raise ValueError(f"Unknown cache option: {cache}")
|
|
103
|
+
|
|
104
|
+
self.client = ClientType(
|
|
105
|
+
wsdl=self.wsdl_path,
|
|
106
|
+
transport=transport,
|
|
107
|
+
settings=settings,
|
|
108
|
+
wsse=wsse,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
if not binding:
|
|
112
|
+
raise ValueError("Bindings must be set according to the WSDL service")
|
|
113
|
+
|
|
114
|
+
self.service = self.client.create_service(binding, self.address)
|
|
115
|
+
logging.debug(f"ONVIFCore initialized {binding} at {self.address}")
|
|
116
|
+
|
|
117
|
+
def call(self, method: str, *args, **kwargs):
|
|
118
|
+
try:
|
|
119
|
+
func = getattr(self.service, method)
|
|
120
|
+
except AttributeError as e:
|
|
121
|
+
raise ONVIFOperationException(operation=method, original_exception=e)
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
return func(*args, **kwargs)
|
|
125
|
+
except Fault as e:
|
|
126
|
+
# logging.error(f"SOAP Fault in {method}: {e}")
|
|
127
|
+
raise ONVIFOperationException(operation=method, original_exception=e)
|
|
128
|
+
except Exception as e:
|
|
129
|
+
# logging.error(f"ONVIF call error in {method}: {e}")
|
|
130
|
+
raise ONVIFOperationException(operation=method, original_exception=e)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from .devicemgmt import Device
|
|
2
|
+
from .events.events import Events
|
|
3
|
+
from .events.pullpoint import PullPoint
|
|
4
|
+
from .events.notification import Notification
|
|
5
|
+
from .events.subscription import Subscription
|
|
6
|
+
from .imaging import Imaging
|
|
7
|
+
from .media import Media
|
|
8
|
+
from .media2 import Media2
|
|
9
|
+
from .ptz import PTZ
|
|
10
|
+
from .accesscontrol import AccessControl
|
|
11
|
+
from .accessrules import AccessRules
|
|
12
|
+
from .actionengine import ActionEngine
|
|
13
|
+
from .analytics.analytics import Analytics
|
|
14
|
+
from .analytics.ruleengine import RuleEngine
|
|
15
|
+
from .analyticsdevice import AnalyticsDevice
|
|
16
|
+
from .appmgmt import AppManagement
|
|
17
|
+
from .authenticationbehavior import AuthenticationBehavior
|
|
18
|
+
from .credential import Credential
|
|
19
|
+
from .deviceio import DeviceIO
|
|
20
|
+
from .display import Display
|
|
21
|
+
from .doorcontrol import DoorControl
|
|
22
|
+
from .provisioning import Provisioning
|
|
23
|
+
from .receiver import Receiver
|
|
24
|
+
from .recording import Recording
|
|
25
|
+
from .replay import Replay
|
|
26
|
+
from .schedule import Schedule
|
|
27
|
+
from .search import Search
|
|
28
|
+
from .thermal import Thermal
|
|
29
|
+
from .uplink import Uplink
|
|
30
|
+
from .security.advancedsecurity import AdvancedSecurity
|
|
31
|
+
from .security.jwt import JWT
|
|
32
|
+
from .security.keystore import Keystore
|
|
33
|
+
from .security.tlsserver import TLSServer
|
|
34
|
+
from .security.dot1x import Dot1X
|
|
35
|
+
from .security.authorizationserver import AuthorizationServer
|
|
36
|
+
from .security.mediasigning import MediaSigning
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"Device",
|
|
40
|
+
"Events",
|
|
41
|
+
"PullPoint",
|
|
42
|
+
"Notification",
|
|
43
|
+
"Subscription",
|
|
44
|
+
"Imaging",
|
|
45
|
+
"Media",
|
|
46
|
+
"Media2",
|
|
47
|
+
"PTZ",
|
|
48
|
+
"AccessControl",
|
|
49
|
+
"AccessRules",
|
|
50
|
+
"ActionEngine",
|
|
51
|
+
"Analytics",
|
|
52
|
+
"RuleEngine",
|
|
53
|
+
"AnalyticsDevice",
|
|
54
|
+
"AppManagement",
|
|
55
|
+
"AuthenticationBehavior",
|
|
56
|
+
"Credential",
|
|
57
|
+
"DeviceIO",
|
|
58
|
+
"Display",
|
|
59
|
+
"DoorControl",
|
|
60
|
+
"Provisioning",
|
|
61
|
+
"Receiver",
|
|
62
|
+
"Recording",
|
|
63
|
+
"Replay",
|
|
64
|
+
"Schedule",
|
|
65
|
+
"Search",
|
|
66
|
+
"Thermal",
|
|
67
|
+
"Uplink",
|
|
68
|
+
"AdvancedSecurity",
|
|
69
|
+
"JWT",
|
|
70
|
+
"Keystore",
|
|
71
|
+
"TLSServer",
|
|
72
|
+
"Dot1X",
|
|
73
|
+
"AuthorizationServer",
|
|
74
|
+
"MediaSigning",
|
|
75
|
+
]
|