Skip to content

Commit ed37d16

Browse files
Merge branch 'zeep' into pr/async-services
2 parents 8aba823 + 33f8051 commit ed37d16

File tree

3 files changed

+142
-125
lines changed

3 files changed

+142
-125
lines changed

onvif/client.py

Lines changed: 83 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,40 @@
11
from __future__ import print_function, division
22
__version__ = '0.0.1'
3+
import datetime as dt
4+
import logging
35
import os.path
46
from threading import Thread, RLock
57

6-
import logging
7-
logger = logging.getLogger('onvif')
8-
logging.basicConfig(level=logging.INFO)
9-
logging.getLogger('zeep.client').setLevel(logging.CRITICAL)
10-
118
from zeep.client import Client, CachingClient, Settings
129
from zeep.wsse.username import UsernameToken
1310
import zeep.helpers
1411

1512
from onvif.exceptions import ONVIFError
1613
from onvif.definition import SERVICES
17-
import datetime as dt
14+
15+
logger = logging.getLogger('onvif')
16+
logging.basicConfig(level=logging.INFO)
17+
logging.getLogger('zeep.client').setLevel(logging.CRITICAL)
18+
19+
1820
# Ensure methods to raise an ONVIFError Exception
1921
# when some thing was wrong
2022
def safe_func(func):
2123
def wrapped(*args, **kwargs):
2224
try:
2325
return func(*args, **kwargs)
2426
except Exception as err:
25-
#print('Ouuups: err =', err, ', func =', func, ', args =', args, ', kwargs =', kwargs)
2627
raise ONVIFError(err)
2728
return wrapped
2829

2930

3031
class UsernameDigestTokenDtDiff(UsernameToken):
31-
'''
32+
"""
3233
UsernameDigestToken class, with a time offset parameter that can be adjusted;
3334
This allows authentication on cameras without being time synchronized.
3435
Please note that using NTP on both end is the recommended solution,
3536
this should only be used in "safe" environments.
36-
'''
37+
"""
3738
def __init__(self, user, passw, dt_diff=None, **kwargs):
3839
super().__init__(user, passw, **kwargs)
3940
self.dt_diff = dt_diff # Date/time difference in datetime.timedelta
@@ -42,17 +43,15 @@ def apply(self, envelope, headers):
4243
old_created = self.created
4344
if self.created is None:
4445
self.created = dt.datetime.utcnow()
45-
#print('UsernameDigestTokenDtDiff.created: old = %s (type = %s), dt_diff = %s (type = %s)' % (self.created, type(self.created), self.dt_diff, type(self.dt_diff)), end='')
4646
if self.dt_diff is not None:
4747
self.created += self.dt_diff
48-
#print(' new = %s' % self.created)
4948
result = super().apply(envelope, headers)
5049
self.created = old_created
5150
return result
5251

5352

5453
class ONVIFService(object):
55-
'''
54+
"""
5655
Python Implemention for ONVIF Service.
5756
Services List:
5857
DeviceMgmt DeviceIO Event AnalyticsDevice Display Imaging Media
@@ -80,12 +79,12 @@ class ONVIFService(object):
8079
params = device_service.create_type('SetHostname')
8180
params.Hostname = 'NewHostName'
8281
device_service.SetHostname(params)
83-
'''
82+
"""
8483

8584
@safe_func
8685
def __init__(self, xaddr, user, passwd, url,
8786
encrypt=True, daemon=False, zeep_client=None, no_cache=False,
88-
portType=None, dt_diff=None, binding_name='', transport=None):
87+
dt_diff=None, binding_name='', transport=None):
8988
if not os.path.isfile(url):
9089
raise ONVIFError('%s doesn`t exist!' % url)
9190

@@ -94,7 +93,6 @@ def __init__(self, xaddr, user, passwd, url,
9493
wsse = UsernameDigestTokenDtDiff(user, passwd, dt_diff=dt_diff, use_digest=encrypt)
9594
# Create soap client
9695
if not zeep_client:
97-
#print(self.url, self.xaddr)
9896
ClientType = Client if no_cache else CachingClient
9997
settings = Settings()
10098
settings.strict = False
@@ -139,7 +137,6 @@ def call(params=None, callback=None):
139137
try:
140138
ret = func(**params)
141139
except TypeError:
142-
#print('### func =', func, '### params =', params, '### type(params) =', type(params))
143140
ret = func(params)
144141
if callable(callback):
145142
callback(ret)
@@ -154,23 +151,23 @@ def call(params=None, callback=None):
154151
return wrapped
155152

156153
def __getattr__(self, name):
157-
'''
154+
"""
158155
Call the real onvif Service operations,
159156
See the official wsdl definition for the
160157
APIs detail(API name, request parameters,
161158
response parameters, parameter types, etc...)
162-
'''
163-
builtin = name.startswith('__') and name.endswith('__')
159+
"""
160+
builtin = name.startswith('__') and name.endswith('__')
164161
if builtin:
165162
return self.__dict__[name]
166163
else:
167164
return self.service_wrapper(getattr(self.ws_client, name))
168165

169166

170167
class ONVIFCamera(object):
171-
'''
172-
Python Implemention ONVIF compliant device
173-
This class integrates onvif services
168+
"""
169+
Python Implementation of an ONVIF compliant device.
170+
This class integrates ONVIF services
174171
175172
adjust_time parameter allows authentication on cameras without being time synchronized.
176173
Please note that using NTP on both end is the recommended solution,
@@ -183,18 +180,20 @@ class ONVIFCamera(object):
183180
>>> media_service = mycam.create_media_service()
184181
>>> ptz_service = mycam.create_ptz_service()
185182
# Get PTZ Configuration:
186-
>>> mycam.ptz.GetConfiguration()
187-
# Another way:
188183
>>> ptz_service.GetConfiguration()
189-
'''
184+
"""
190185

191186
# Class-level variables
192187
services_template = {'devicemgmt': None, 'ptz': None, 'media': None,
193-
'imaging': None, 'events': None, 'analytics': None }
188+
'imaging': None, 'events': None, 'analytics': None}
194189
use_services_template = {'devicemgmt': True, 'ptz': True, 'media': True,
195-
'imaging': True, 'events': True, 'analytics': True }
196-
def __init__(self, host, port ,user, passwd, wsdl_dir=os.path.join(os.path.dirname(os.path.dirname(__file__)), "wsdl"),
197-
encrypt=True, daemon=False, no_cache=False, adjust_time=False, transport=None):
190+
'imaging': True, 'events': True, 'analytics': True}
191+
192+
def __init__(self, host, port, user, passwd,
193+
wsdl_dir=os.path.join(os.path.dirname(os.path.dirname(__file__)),
194+
"wsdl"),
195+
encrypt=True, daemon=False, no_cache=False, adjust_time=False,
196+
transport=None):
198197
os.environ.pop('http_proxy', None)
199198
os.environ.pop('https_proxy', None)
200199
self.host = host
@@ -209,7 +208,7 @@ def __init__(self, host, port ,user, passwd, wsdl_dir=os.path.join(os.path.dirna
209208
self.transport = transport
210209

211210
# Active service client container
212-
self.services = { }
211+
self.services = {}
213212
self.services_lock = RLock()
214213

215214
# Set xaddrs
@@ -220,16 +219,16 @@ def __init__(self, host, port ,user, passwd, wsdl_dir=os.path.join(os.path.dirna
220219
def update_xaddrs(self):
221220
# Establish devicemgmt service first
222221
self.dt_diff = None
223-
self.devicemgmt = self.create_devicemgmt_service()
224-
if self.adjust_time :
222+
self.devicemgmt = self.create_devicemgmt_service()
223+
if self.adjust_time:
225224
cdate = self.devicemgmt.GetSystemDateAndTime().UTCDateTime
226-
cam_date = dt.datetime(cdate.Date.Year, cdate.Date.Month, cdate.Date.Day, cdate.Time.Hour, cdate.Time.Minute, cdate.Time.Second)
225+
cam_date = dt.datetime(cdate.Date.Year, cdate.Date.Month, cdate.Date.Day,
226+
cdate.Time.Hour, cdate.Time.Minute, cdate.Time.Second)
227227
self.dt_diff = cam_date - dt.datetime.utcnow()
228228
self.devicemgmt.dt_diff = self.dt_diff
229-
#self.devicemgmt.set_wsse()
230-
self.devicemgmt = self.create_devicemgmt_service()
229+
self.devicemgmt = self.create_devicemgmt_service()
231230
# Get XAddr of services on the device
232-
self.xaddrs = { }
231+
self.xaddrs = {}
233232
capabilities = self.devicemgmt.GetCapabilities({'Category': 'All'})
234233
for name in capabilities:
235234
capability = capabilities[name]
@@ -243,8 +242,9 @@ def update_xaddrs(self):
243242
with self.services_lock:
244243
try:
245244
self.event = self.create_events_service()
246-
self.xaddrs['http://www.onvif.org/ver10/events/wsdl/PullPointSubscription'] = self.event.CreatePullPointSubscription().SubscriptionReference.Address._value_1
247-
except:
245+
self.xaddrs['http://www.onvif.org/ver10/events/wsdl/PullPointSubscription'] = \
246+
self.event.CreatePullPointSubscription().SubscriptionReference.Address._value_1
247+
except Exception:
248248
pass
249249

250250
def update_url(self, host=None, port=None):
@@ -268,14 +268,13 @@ def update_url(self, host=None, port=None):
268268
self.services[sname].ws_client.set_options(location=xaddr)
269269

270270
def get_service(self, name, create=True):
271-
service = None
272271
service = getattr(self, name.lower(), None)
273272
if not service and create:
274273
return getattr(self, 'create_%s_service' % name.lower())()
275274
return service
276275

277276
def get_definition(self, name, portType=None):
278-
'''Returns xaddr and wsdl of specified service'''
277+
"""Returns xaddr and wsdl of specified service"""
279278
# Check if the service is supported
280279
if name not in SERVICES:
281280
raise ONVIFError('Unknown service %s' % name)
@@ -301,12 +300,21 @@ def get_definition(self, name, portType=None):
301300
# Get other XAddr
302301
xaddr = self.xaddrs.get(ns)
303302
if not xaddr:
304-
raise ONVIFError('Device doesn`t support service: %s' % name)
303+
raise ONVIFError("Device doesn't support service: %s" % name)
305304

306305
return xaddr, wsdlpath, binding_name
307306

308-
def create_onvif_service(self, name, from_template=True, portType=None, transport=None):
309-
'''Create ONVIF service client'''
307+
def create_onvif_service(self, name, portType=None, transport=None):
308+
"""
309+
Create ONVIF service client.
310+
311+
:param name: service name, should be present as a key within
312+
the `SERVICES` dictionary declared within the `onvif.definition` module
313+
:param portType:
314+
:param transport:
315+
:return:
316+
"""
317+
"""Create ONVIF service client"""
310318

311319
name = name.lower()
312320
xaddr, wsdl_file, binding_name = self.get_definition(name, portType)
@@ -318,7 +326,6 @@ def create_onvif_service(self, name, from_template=True, portType=None, transpor
318326
service = ONVIFService(xaddr, self.user, self.passwd,
319327
wsdl_file, self.encrypt,
320328
self.daemon, no_cache=self.no_cache,
321-
portType=portType,
322329
dt_diff=self.dt_diff,
323330
binding_name=binding_name,
324331
transport=transport)
@@ -331,41 +338,47 @@ def create_onvif_service(self, name, from_template=True, portType=None, transpor
331338

332339
return service
333340

334-
def create_devicemgmt_service(self, from_template=True, transport=None):
341+
def create_devicemgmt_service(self, transport=None):
335342
# The entry point for devicemgmt service is fixed.
336-
return self.create_onvif_service('devicemgmt', from_template, transport=transport)
343+
return self.create_onvif_service('devicemgmt', transport=transport)
344+
345+
def create_media_service(self, transport=None):
346+
return self.create_onvif_service('media', transport=transport)
347+
348+
def create_ptz_service(self, transport=None):
349+
return self.create_onvif_service('ptz', transport=transport)
337350

338-
def create_media_service(self, from_template=True, transport=None):
339-
return self.create_onvif_service('media', from_template, transport=transport)
351+
def create_imaging_service(self, transport=None):
352+
return self.create_onvif_service('imaging', transport=transport)
340353

341-
def create_ptz_service(self, from_template=True, transport=None):
342-
return self.create_onvif_service('ptz', from_template, transport=transport)
354+
def create_deviceio_service(self, transport=None):
355+
return self.create_onvif_service('deviceio', transport=transport)
343356

344-
def create_imaging_service(self, from_template=True, transport=None):
345-
return self.create_onvif_service('imaging', from_template, transport=transport)
357+
def create_events_service(self, transport=None):
358+
return self.create_onvif_service('events', transport=transport)
346359

347-
def create_deviceio_service(self, from_template=True, transport=None):
348-
return self.create_onvif_service('deviceio', from_template, transport=transport)
360+
def create_analytics_service(self, transport=None):
361+
return self.create_onvif_service('analytics', transport=transport)
349362

350-
def create_events_service(self, from_template=True, transport=None):
351-
return self.create_onvif_service('events', from_template, transport=transport)
363+
def create_recording_service(self, transport=None):
364+
return self.create_onvif_service('recording', transport=transport)
352365

353-
def create_analytics_service(self, from_template=True, transport=None):
354-
return self.create_onvif_service('analytics', from_template, transport=transport)
366+
def create_search_service(self, transport=None):
367+
return self.create_onvif_service('search', transport=transport)
355368

356-
def create_recording_service(self, from_template=True, transport=None):
357-
return self.create_onvif_service('recording', from_template, transport=transport)
369+
def create_replay_service(self, transport=None):
370+
return self.create_onvif_service('replay', transport=transport)
358371

359-
def create_search_service(self, from_template=True, transport=None):
360-
return self.create_onvif_service('search', from_template, transport=transport)
372+
def create_pullpoint_service(self, transport=None):
373+
return self.create_onvif_service('pullpoint',
374+
portType='PullPointSubscription',
375+
transport=transport)
361376

362-
def create_replay_service(self, from_template=True, transport=None):
363-
return self.create_onvif_service('replay', from_template, transport=transport)
377+
def create_receiver_service(self, transport=None):
378+
return self.create_onvif_service('receiver', transport=transport)
364379

365-
def create_pullpoint_service(self, from_template=True, transport=None):
366-
return self.create_onvif_service('pullpoint', from_template,
367-
portType='PullPointSubscription',
368-
transport=transport)
380+
def create_notification_service(self, transport=None):
381+
return self.create_onvif_service('notification', transport=transport)
369382

370-
def create_receiver_service(self, from_template=True, transport=None):
371-
return self.create_onvif_service('receiver', from_template, transport=transport)
383+
def create_subscription_service(self, transport=None):
384+
return self.create_onvif_service('subscription', transport=transport)

onvif/definition.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
SERVICES = {
22
# Name namespace wsdl file binding name
3-
'devicemgmt': {'ns': 'http://www.onvif.org/ver10/device/wsdl', 'wsdl': 'devicemgmt.wsdl', 'binding' : 'DeviceBinding'},
4-
'media' : {'ns': 'http://www.onvif.org/ver10/media/wsdl', 'wsdl': 'media.wsdl', 'binding' : 'MediaBinding'},
5-
'ptz' : {'ns': 'http://www.onvif.org/ver20/ptz/wsdl', 'wsdl': 'ptz.wsdl', 'binding' : 'PTZBinding'},
6-
'imaging' : {'ns': 'http://www.onvif.org/ver20/imaging/wsdl', 'wsdl': 'imaging.wsdl', 'binding' : 'ImagingBinding'},
7-
'deviceio' : {'ns': 'http://www.onvif.org/ver10/deviceIO/wsdl', 'wsdl': 'deviceio.wsdl', 'binding' : 'DeviceIOBinding'},
8-
'events' : {'ns': 'http://www.onvif.org/ver10/events/wsdl', 'wsdl': 'events.wsdl', 'binding' : 'EventBinding'},
9-
'pullpoint' : {'ns': 'http://www.onvif.org/ver10/events/wsdl', 'wsdl': 'events.wsdl', 'binding' : 'PullPointSubscriptionBinding'},
10-
'analytics' : {'ns': 'http://www.onvif.org/ver20/analytics/wsdl', 'wsdl': 'analytics.wsdl', 'binding' : 'AnalyticsEngineBinding'},
11-
'recording' : {'ns': 'http://www.onvif.org/ver10/recording/wsdl', 'wsdl': 'recording.wsdl', 'binding' : 'RecordingBinding'},
12-
'search' : {'ns': 'http://www.onvif.org/ver10/search/wsdl', 'wsdl': 'search.wsdl', 'binding' : 'SearchBinding'},
13-
'replay' : {'ns': 'http://www.onvif.org/ver10/replay/wsdl', 'wsdl': 'replay.wsdl', 'binding' : 'ReplayBinding'},
14-
'receiver' : {'ns': 'http://www.onvif.org/ver10/receiver/wsdl', 'wsdl': 'receiver.wsdl', 'binding' : 'ReceiverBinding'},
3+
'devicemgmt' : {'ns': 'http://www.onvif.org/ver10/device/wsdl', 'wsdl': 'devicemgmt.wsdl', 'binding' : 'DeviceBinding'},
4+
'media' : {'ns': 'http://www.onvif.org/ver10/media/wsdl', 'wsdl': 'media.wsdl', 'binding' : 'MediaBinding'},
5+
'ptz' : {'ns': 'http://www.onvif.org/ver20/ptz/wsdl', 'wsdl': 'ptz.wsdl', 'binding' : 'PTZBinding'},
6+
'imaging' : {'ns': 'http://www.onvif.org/ver20/imaging/wsdl', 'wsdl': 'imaging.wsdl', 'binding' : 'ImagingBinding'},
7+
'deviceio' : {'ns': 'http://www.onvif.org/ver10/deviceIO/wsdl', 'wsdl': 'deviceio.wsdl', 'binding' : 'DeviceIOBinding'},
8+
'events' : {'ns': 'http://www.onvif.org/ver10/events/wsdl', 'wsdl': 'events.wsdl', 'binding' : 'EventBinding'},
9+
'pullpoint' : {'ns': 'http://www.onvif.org/ver10/events/wsdl', 'wsdl': 'events.wsdl', 'binding' : 'PullPointSubscriptionBinding'},
10+
'notification' : {'ns': 'http://www.onvif.org/ver10/events/wsdl', 'wsdl': 'events.wsdl', 'binding' : 'NotificationProducerBinding'},
11+
'subscription' : {'ns': 'http://www.onvif.org/ver10/events/wsdl', 'wsdl': 'events.wsdl', 'binding' : 'SubscriptionManagerBinding'},
12+
'analytics' : {'ns': 'http://www.onvif.org/ver20/analytics/wsdl', 'wsdl': 'analytics.wsdl', 'binding' : 'AnalyticsEngineBinding'},
13+
'recording' : {'ns': 'http://www.onvif.org/ver10/recording/wsdl', 'wsdl': 'recording.wsdl', 'binding' : 'RecordingBinding'},
14+
'search' : {'ns': 'http://www.onvif.org/ver10/search/wsdl', 'wsdl': 'search.wsdl', 'binding' : 'SearchBinding'},
15+
'replay' : {'ns': 'http://www.onvif.org/ver10/replay/wsdl', 'wsdl': 'replay.wsdl', 'binding' : 'ReplayBinding'},
16+
'receiver' : {'ns': 'http://www.onvif.org/ver10/receiver/wsdl', 'wsdl': 'receiver.wsdl', 'binding' : 'ReceiverBinding'},
1517
}
1618

1719
#

0 commit comments

Comments
 (0)