onvif-python 0.0.1__py3-none-any.whl → 0.0.2__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 CHANGED
@@ -3,6 +3,7 @@
3
3
  from .client import ONVIFClient
4
4
  from .operator import ONVIFOperator, CacheMode
5
5
  from .utils import ONVIFWSDL, ONVIFOperationException
6
+ from .utils.zeep import apply_patch
6
7
 
7
8
  __all__ = [
8
9
  "ONVIFClient",
@@ -11,3 +12,5 @@ __all__ = [
11
12
  "ONVIFWSDL",
12
13
  "ONVIFOperationException",
13
14
  ]
15
+
16
+ apply_patch()
onvif/client.py CHANGED
@@ -130,32 +130,45 @@ class ONVIFClient:
130
130
  self._authorizationserver = None
131
131
  self._mediasigning = None
132
132
 
133
+
133
134
  def _get_xaddr(self, service_name: str, service_path: str):
134
135
  """
135
136
  Resolve XAddr from GetCapabilities. Fallback to default if not present.
137
+ Now supports nested Extension with _value_1 dict (after normalized parsing).
136
138
  """
137
139
  svc = getattr(self.capabilities, service_name, None)
138
- if svc and hasattr(svc, "XAddr") and svc.XAddr:
139
- parsed = urlparse(svc.XAddr)
140
140
 
141
+ # Step 1: check direct attribute (e.g. capabilities.Media)
142
+ if svc and hasattr(svc, "XAddr"):
143
+ xaddr = svc.XAddr
144
+ else:
145
+ # Step 2: try legacy Extension.service_name
146
+ ext = getattr(self.capabilities, "Extension", None)
147
+ if ext and hasattr(ext, service_name):
148
+ svc = getattr(ext, service_name, None)
149
+ xaddr = getattr(svc, "XAddr", None) if svc else None
150
+ else:
151
+ # Step 3: try new-style _value_1 dict inside Extension
152
+ ext_dict = getattr(ext, "_value_1", {})
153
+ xaddr = ext_dict.get(service_name, {}).get("XAddr") if isinstance(ext_dict, dict) else None
154
+
155
+ if xaddr:
156
+ parsed = urlparse(xaddr)
141
157
  # Host/port from device
142
158
  device_host = parsed.hostname
143
159
  device_port = parsed.port
144
-
145
160
  # Host/port from connection
146
161
  connect_host = self.common_args["host"]
147
162
  connect_port = self.common_args["port"]
148
-
149
163
  # if host/port differ, rewrite XAddr to use connection values
150
164
  if (device_host != connect_host) or (device_port != connect_port):
151
- protocol = parsed.scheme
165
+ protocol = "https" if self.common_args["use_https"] else "http"
152
166
  new_netloc = f"{connect_host}:{connect_port}"
153
167
  rewritten = urlunparse((protocol, new_netloc, parsed.path, "", "", ""))
154
168
  return rewritten
169
+ return xaddr
155
170
 
156
- return svc.XAddr
157
-
158
- # fallback (legacy style)
171
+ # Fallback default
159
172
  protocol = "https" if self.common_args["use_https"] else "http"
160
173
  return f"{protocol}://{self.common_args['host']}:{self.common_args['port']}/onvif/{service_path}"
161
174
 
@@ -16,9 +16,7 @@ class Device:
16
16
  )
17
17
 
18
18
  def GetServices(self, IncludeCapability):
19
- return self.operator.call(
20
- "GetServices", IncludeCapability=IncludeCapability
21
- )
19
+ return self.operator.call("GetServices", IncludeCapability=IncludeCapability)
22
20
 
23
21
  def GetServiceCapabilities(self):
24
22
  return self.operator.call("GetServiceCapabilities")
onvif/services/search.py CHANGED
@@ -10,7 +10,7 @@ class Search:
10
10
  self.operator = ONVIFOperator(
11
11
  definition["path"],
12
12
  binding=f"{{{definition['namespace']}}}{definition['binding']}",
13
- service_path="Search", # fallback
13
+ service_path="SearchRecording", # fallback
14
14
  xaddr=xaddr,
15
15
  **kwargs,
16
16
  )
onvif/utils/zeep.py ADDED
@@ -0,0 +1,49 @@
1
+ # onvif/utils/zeep.py
2
+
3
+ from lxml.etree import QName
4
+ from zeep.xsd.elements.any import Any
5
+ from zeep.xsd.utils import max_occurs_iter
6
+
7
+
8
+ def patched_parse_xmlelements(self, xmlelements, schema, name=None, context=None):
9
+ """Patched version of parse_xmlelements for <xsd:any> to support child parsing."""
10
+ parsed_result = {}
11
+
12
+ for _ in max_occurs_iter(self.max_occurs):
13
+ if not xmlelements:
14
+ break
15
+
16
+ xmlelement = xmlelements.popleft()
17
+ tag_name = QName(xmlelement.tag).localname
18
+ children = list(xmlelement)
19
+
20
+ if children and schema:
21
+ child_result = {}
22
+ for child in children:
23
+ child_qname = QName(child.tag)
24
+ try:
25
+ xsd_el = schema.get_element(child_qname)
26
+ val = xsd_el.parse_xmlelement(
27
+ child, schema=schema, allow_none=True, context=context
28
+ )
29
+ child_result[child_qname.localname] = val
30
+ except Exception:
31
+ if list(child): # has nested children
32
+ nested = {
33
+ QName(sub.tag).localname: sub.text or sub
34
+ for sub in child
35
+ }
36
+ child_result[child_qname.localname] = nested
37
+ else:
38
+ child_result[child_qname.localname] = child.text or child
39
+
40
+ parsed_result[tag_name] = child_result
41
+ else:
42
+ parsed_result[tag_name] = xmlelement.text or xmlelement
43
+
44
+ return parsed_result
45
+
46
+
47
+ def apply_patch():
48
+ """Inject the custom parse_xmlelements method into zeep.xsd.elements.any.Any."""
49
+ Any.parse_xmlelements = patched_parse_xmlelements
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onvif-python
3
- Version: 0.0.1
3
+ Version: 0.0.2
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
@@ -44,7 +44,7 @@ Dynamic: license-file
44
44
  [![Release](https://img.shields.io/badge/Release-v0.0.1-green?logo=archive)](https://github.com/nirsimetri/onvif-python/releases)
45
45
  <br>
46
46
  [![PyPI](https://img.shields.io/badge/PyPI-0.0.1-yellow?logo=archive)](https://pypi.org/project/onvif-python/)
47
- [![Downloads](https://img.shields.io/pypi/dm/onvif-python?label=PyPI%20Downloads)](https://pypi.org/project/onvif-python/)
47
+ [![Downloads](https://img.shields.io/pypi/dd/onvif-python?label=PyPI%20Downloads&color=white)](https://pypi.org/project/onvif-python/)
48
48
 
49
49
  Are you having trouble finding a Python ONVIF library that supports your device?
50
50
  Are you confused about the compatibility of the various ONVIF versions, which are updated every six months?
@@ -108,11 +108,8 @@ client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
108
108
  - `client.events()` — Events
109
109
  - `client.imaging()` — Imaging
110
110
  - `client.media()` — Media
111
- - `client.media2()` — Media 2
112
111
  - `client.ptz()` — PTZ (Pan-Tilt-Zoom)
113
- - `client.deviceio()` — Device IO
114
112
  - `client.analytics()` — Analytics
115
- - `client.recording()` — Recording
116
113
 
117
114
  and so on, check [Implemented ONVIF Services](https://github.com/nirsimetri/onvif-python?tab=readme-ov-file#implemented-onvif-services) for more details
118
115
 
@@ -200,7 +197,7 @@ This library fully supports all major ONVIF Profiles listed below. Each profile
200
197
  | Profile_T | [Document](https://www.onvif.org/wp-content/uploads/2018/09/ONVIF_Profile_T_Specification_v1-0.pdf) | Advanced video streaming (H.265, analytics metadata, motion detection) | Modern cameras and clients | ✅ Yes |
201
198
  | Profile_C | [Document](https://www.onvif.org/wp-content/uploads/2017/01/2013_12_ONVIF_Profile_C_Specification_v1-0.pdf) | Access control, door monitoring | Door controllers, access systems | ✅ Yes |
202
199
  | Profile_A | [Document](https://www.onvif.org/wp-content/uploads/2017/06/ONVIF_Profile_A_Specification_v1-0.pdf) | Advanced access control configuration, credential management | Access control clients and devices | ✅ Yes |
203
- | Profile_D | [Document](https://www.onvif.org/wp-content/uploads/2017/06/ONVIF_Profile_D_Specification_v1-0.pdf) | Access control peripherals (locks, sensors, relays) | Peripheral devices for access control | ✅ Yes |
200
+ | Profile_D | [Document](https://www.onvif.org/wp-content/uploads/2021/06/onvif-profile-d-specification-v1-0.pdf) | Access control peripherals (locks, sensors, relays) | Peripheral devices for access control | ✅ Yes |
204
201
  | Profile_M | [Document](https://www.onvif.org/wp-content/uploads/2024/04/onvif-profile-m-specification-v1-1.pdf) | Metadata, analytics events, object detection | Analytics devices, metadata clients | ✅ Yes |
205
202
 
206
203
  For a full description of each profile and its features, visit [ONVIF Profiles](https://www.onvif.org/profiles/).
@@ -1,5 +1,5 @@
1
- onvif/__init__.py,sha256=paiszXL_-uJRVH2l5EIUwJQMwWNTnfE1uKLnbmvBLxo,287
2
- onvif/client.py,sha256=8xllz9bJc7LfdpRFL97rYO4na66UCkbuLzfcQjYAMmY,13929
1
+ onvif/__init__.py,sha256=HKu0PTXn3sLwS6EzyfgnPsrtP8_02MhUGnn6JDgRK8w,339
2
+ onvif/client.py,sha256=0ENBKyzqzBMncUZrN219kdQPOBYVNJkOlBKfzmmof-4,14713
3
3
  onvif/operator.py,sha256=DawfvCMvQIvNREvYazonhauvE66bdV93iMn586WKrk8,4756
4
4
  onvif/services/__init__.py,sha256=1sxD3pbUOSXu27GBeLn3CW_VA9JRfiG7dVjFpfFyQME,2050
5
5
  onvif/services/accesscontrol.py,sha256=uGqMHErBS8fHPMWKhDyxBs9ueCCoDjRFsdBMGtndL7M,3804
@@ -10,7 +10,7 @@ onvif/services/appmgmt.py,sha256=5ytUiNPr6FtZChXLIzVWQ_vr3lbqE4mSxNhqaClf-QA,129
10
10
  onvif/services/authenticationbehavior.py,sha256=uUMHu4DBCUAoG-0eQeZFgBs5PmXXqsIh2COY3FdGniY,3278
11
11
  onvif/services/credential.py,sha256=e2W4IJInmuw5OdOkUt1uQ_LpUQcgGVOFgJcZqeGxLKw,5590
12
12
  onvif/services/deviceio.py,sha256=mB5GFh5u6pmjb3Ss0U-nJdMfmewh30PK30ao3mqGXbg,5922
13
- onvif/services/devicemgmt.py,sha256=BYZb32wAzkwZdUFlO5eQlMzyfAPzdN4gGea3qOM9Sqs,14543
13
+ onvif/services/devicemgmt.py,sha256=LmizOoZwfNLtTEOEdk2ialEmrNyC2FvduND9iHeP3U4,14519
14
14
  onvif/services/display.py,sha256=QNq4FSh9_x6nwizansShaF4O3A0TwGbc84Q62FW-FLk,2218
15
15
  onvif/services/doorcontrol.py,sha256=4-gPizy84-e79JhU44AWcaVlvikkT7NHLo9ZgN3sKFs,2981
16
16
  onvif/services/imaging.py,sha256=aSukKYZVA1aHV958hVX75TLT5zvLVqAwkb3oXuo69Jw,2268
@@ -22,7 +22,7 @@ onvif/services/receiver.py,sha256=f83D5MudSN1muFoS1-EhbzrrzDJSYcsthCb7X5AiJgg,15
22
22
  onvif/services/recording.py,sha256=clnfF0LlZA7vO2WBOz62_7LtnOjo9UBZEBRFF-QDDz0,4764
23
23
  onvif/services/replay.py,sha256=wydnAp-qjN-fn-YkILA9G4Gf700FGBZjBMM17NrXdOk,1022
24
24
  onvif/services/schedule.py,sha256=u59qFBDMmSjcq6vgn15mUkKm3RLBplkT_bI6O-9TB4c,3006
25
- onvif/services/search.py,sha256=VfrHkKftGyjBRrbJylga9cnzwIrCCF1aCxQEXcH4aBk,4438
25
+ onvif/services/search.py,sha256=KQ_Y95lSMc3JfgbekEnZyNhRtYOlI77gOPbFM5mIQI8,4447
26
26
  onvif/services/thermal.py,sha256=HgJTnTAUvRSaQeyOtMhQw0SSb7QNTnCwPGxs5C95fMw,1887
27
27
  onvif/services/uplink.py,sha256=OJmZEk45xBDrPyzxJjGD0poGMxmF86ub4c0VaRIME1U,904
28
28
  onvif/services/analytics/analytics.py,sha256=NHPxQ7HlCGNU2CqA_n7nmqQIr-9HyRDul7v6ACv9_fc,2150
@@ -41,6 +41,7 @@ onvif/services/security/tlsserver.py,sha256=R8gTkC-tb1mtI5bke_UxIkNyNqoWnXpdCH4J
41
41
  onvif/utils/__init__.py,sha256=x8iQY4t74l5HgZVjNHUy9WlRX5cixtKXW9-L5VrBs5g,132
42
42
  onvif/utils/exceptions.py,sha256=SlK9hG6K41lpXL0cglundpZ2ZlzBlgl6_fDZVv2IzEs,1173
43
43
  onvif/utils/wsdl.py,sha256=V_Y1ucWeE_-VEtq7h3mJftSRD7NNFaBZgAR_QW4NyK8,11550
44
+ onvif/utils/zeep.py,sha256=v3XZc4HXS_dLKdrA7VwB4ilifeaUKK3dlxZMY09Lb-Y,1807
44
45
  onvif/wsdl/ver10/actionengine.wsdl,sha256=9jiO9RtEh4lXKioIoOdE39IPLqp7DeKhdXTg559rOck,57027
45
46
  onvif/wsdl/ver10/analyticsdevice.wsdl,sha256=nMXy58xWSpOxlngK3LHTDXripg3QkAykMBTouALMjaY,31708
46
47
  onvif/wsdl/ver10/deviceio.wsdl,sha256=OHtJzBAoCtV9Lxi3z-vJsT4EOS9wZX5d22PP92Op0Zk,66199
@@ -99,8 +100,8 @@ onvif/wsdl/ver20/util/federated_search_documentation.html,sha256=RD6iCgduQnUL86k
99
100
  onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl,sha256=kaP8PWD59IR46I6N2IM0axn1iUug9v1TRA79vQWTcDM,333
100
101
  onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl,sha256=Ciq_EXijn4nYeSn5XfbdroC2xHG_zgcspw1k1JhKjh0,97104
101
102
  onvif/wsdl/ver20/util/operationIndex.html,sha256=OuioH1LERO_278IKcMvaekQqg7MwbZzU_OxpIlG5d0I,39387
102
- onvif_python-0.0.1.dist-info/licenses/LICENSE.md,sha256=HWZa5heRH0HmiUM5aYDE2KNRV8zJso4KzGYEuMagr14,1111
103
- onvif_python-0.0.1.dist-info/METADATA,sha256=betVMvUvtCmvWZ_iM22CJQwJR5i3UkTZPG0igyuT0Pc,29898
104
- onvif_python-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
105
- onvif_python-0.0.1.dist-info/top_level.txt,sha256=MqCi1uJmIvtVFJDz2IxLbpwq6FwDtWhqrWQtKwLaQg4,6
106
- onvif_python-0.0.1.dist-info/RECORD,,
103
+ onvif_python-0.0.2.dist-info/licenses/LICENSE.md,sha256=HWZa5heRH0HmiUM5aYDE2KNRV8zJso4KzGYEuMagr14,1111
104
+ onvif_python-0.0.2.dist-info/METADATA,sha256=yWdJko41tBQuA3m2QNn6c94T4J7annzpup9vg1bLun8,29802
105
+ onvif_python-0.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
106
+ onvif_python-0.0.2.dist-info/top_level.txt,sha256=MqCi1uJmIvtVFJDz2IxLbpwq6FwDtWhqrWQtKwLaQg4,6
107
+ onvif_python-0.0.2.dist-info/RECORD,,