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.
Files changed (106) hide show
  1. onvif/__init__.py +13 -0
  2. onvif/client.py +485 -0
  3. onvif/operator.py +130 -0
  4. onvif/services/__init__.py +75 -0
  5. onvif/services/accesscontrol.py +105 -0
  6. onvif/services/accessrules.py +47 -0
  7. onvif/services/actionengine.py +46 -0
  8. onvif/services/analytics/analytics.py +60 -0
  9. onvif/services/analytics/ruleengine.py +44 -0
  10. onvif/services/analyticsdevice.py +122 -0
  11. onvif/services/appmgmt.py +40 -0
  12. onvif/services/authenticationbehavior.py +83 -0
  13. onvif/services/credential.py +159 -0
  14. onvif/services/deviceio.py +165 -0
  15. onvif/services/devicemgmt.py +410 -0
  16. onvif/services/display.py +62 -0
  17. onvif/services/doorcontrol.py +93 -0
  18. onvif/services/events/events.py +41 -0
  19. onvif/services/events/notification.py +34 -0
  20. onvif/services/events/pullpoint.py +29 -0
  21. onvif/services/events/subscription.py +21 -0
  22. onvif/services/imaging.py +62 -0
  23. onvif/services/media.py +424 -0
  24. onvif/services/media2.py +310 -0
  25. onvif/services/provisioning.py +50 -0
  26. onvif/services/ptz.py +187 -0
  27. onvif/services/receiver.py +46 -0
  28. onvif/services/recording.py +136 -0
  29. onvif/services/replay.py +30 -0
  30. onvif/services/schedule.py +82 -0
  31. onvif/services/search.py +149 -0
  32. onvif/services/security/advancedsecurity.py +19 -0
  33. onvif/services/security/authorizationserver.py +31 -0
  34. onvif/services/security/dot1x.py +42 -0
  35. onvif/services/security/jwt.py +21 -0
  36. onvif/services/security/keystore.py +208 -0
  37. onvif/services/security/mediasigning.py +30 -0
  38. onvif/services/security/tlsserver.py +82 -0
  39. onvif/services/thermal.py +54 -0
  40. onvif/services/uplink.py +28 -0
  41. onvif/utils/__init__.py +4 -0
  42. onvif/utils/exceptions.py +33 -0
  43. onvif/utils/wsdl.py +297 -0
  44. onvif/wsdl/ver10/accesscontrol/wsdl/index.html +7 -0
  45. onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +702 -0
  46. onvif/wsdl/ver10/accessrules/wsdl/index.html +7 -0
  47. onvif/wsdl/ver10/actionengine.wsdl +1265 -0
  48. onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +4159 -0
  49. onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +7 -0
  50. onvif/wsdl/ver10/analyticsdevice.wsdl +709 -0
  51. onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +502 -0
  52. onvif/wsdl/ver10/appmgmt/wsdl/index.html +7 -0
  53. onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +1382 -0
  54. onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +7 -0
  55. onvif/wsdl/ver10/credential/wsdl/credential.wsdl +2018 -0
  56. onvif/wsdl/ver10/credential/wsdl/index.html +7 -0
  57. onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +4623 -0
  58. onvif/wsdl/ver10/deviceio.wsdl +1414 -0
  59. onvif/wsdl/ver10/display/display.wsdl +383 -0
  60. onvif/wsdl/ver10/display/wsdl/index.htm +383 -0
  61. onvif/wsdl/ver10/display.wsdl +521 -0
  62. onvif/wsdl/ver10/doorcontrol/wsdl/index.html +7 -0
  63. onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +449 -0
  64. onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +947 -0
  65. onvif/wsdl/ver10/events/wsdl/event.wsdl +947 -0
  66. onvif/wsdl/ver10/events/wsdl/index.html +7 -0
  67. onvif/wsdl/ver10/federatedsearch.wsdl +344 -0
  68. onvif/wsdl/ver10/media/wsdl/media.wsdl +3684 -0
  69. onvif/wsdl/ver10/pacs/accesscontrol.wsdl +1942 -0
  70. onvif/wsdl/ver10/pacs/doorcontrol.wsdl +1792 -0
  71. onvif/wsdl/ver10/pacs/types.xsd +181 -0
  72. onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +588 -0
  73. onvif/wsdl/ver10/receiver.wsdl +398 -0
  74. onvif/wsdl/ver10/recording.wsdl +1231 -0
  75. onvif/wsdl/ver10/replay.wsdl +221 -0
  76. onvif/wsdl/ver10/schedule/wsdl/index.html +7 -0
  77. onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +1383 -0
  78. onvif/wsdl/ver10/schema/common.xsd +422 -0
  79. onvif/wsdl/ver10/schema/metadatastream.xsd +520 -0
  80. onvif/wsdl/ver10/schema/onvif.xsd +9251 -0
  81. onvif/wsdl/ver10/search.wsdl +882 -0
  82. onvif/wsdl/ver10/thermal/wsdl/index.html +7 -0
  83. onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +777 -0
  84. onvif/wsdl/ver10/topics/topicns.xml +13 -0
  85. onvif/wsdl/ver10/uplink/wsdl/index.html +7 -0
  86. onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +256 -0
  87. onvif/wsdl/ver20/analytics/humanbody.xsd +490 -0
  88. onvif/wsdl/ver20/analytics/humanface.xsd +501 -0
  89. onvif/wsdl/ver20/analytics/index.html +7 -0
  90. onvif/wsdl/ver20/analytics/radiometry.xsd +389 -0
  91. onvif/wsdl/ver20/analytics/rules.xsd +127 -0
  92. onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +757 -0
  93. onvif/wsdl/ver20/analytics/wsdl/index.html +7 -0
  94. onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +603 -0
  95. onvif/wsdl/ver20/media/wsdl/index.html +7 -0
  96. onvif/wsdl/ver20/media/wsdl/media.wsdl +2862 -0
  97. onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +1450 -0
  98. onvif/wsdl/ver20/util/federated_search_documentation.html +1647 -0
  99. onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +8 -0
  100. onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +2580 -0
  101. onvif/wsdl/ver20/util/operationIndex.html +97 -0
  102. onvif_python-0.0.1.dist-info/METADATA +381 -0
  103. onvif_python-0.0.1.dist-info/RECORD +106 -0
  104. onvif_python-0.0.1.dist-info/WHEEL +5 -0
  105. onvif_python-0.0.1.dist-info/licenses/LICENSE.md +21 -0
  106. 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
+ ]