onvif-python 0.3.0__tar.gz → 0.4.0__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.
Files changed (164) hide show
  1. {onvif_python-0.3.0 → onvif_python-0.4.0}/LICENSE.md +1 -1
  2. onvif_python-0.4.0/PKG-INFO +113 -0
  3. onvif_python-0.4.0/README.md +62 -0
  4. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/__init__.py +7 -3
  5. onvif_python-0.4.0/onvif/cli/helpers/__init__.py +21 -0
  6. onvif_python-0.4.0/onvif/cli/helpers/discovery.py +156 -0
  7. onvif_python-0.4.0/onvif/cli/helpers/execution.py +57 -0
  8. onvif_python-0.4.0/onvif/cli/helpers/files.py +191 -0
  9. onvif_python-0.4.0/onvif/cli/helpers/messages.py +220 -0
  10. onvif_python-0.4.0/onvif/cli/helpers/products.py +294 -0
  11. onvif_python-0.3.0/onvif/cli/utils.py → onvif_python-0.4.0/onvif/cli/helpers/wsdl_parser.py +132 -595
  12. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/cli/interactive.py +52 -212
  13. onvif_python-0.4.0/onvif/cli/main.py +342 -0
  14. onvif_python-0.4.0/onvif/cli/utils.py +510 -0
  15. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/client.py +211 -138
  16. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/meta.py +2 -2
  17. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/operator.py +104 -64
  18. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/accesscontrol.py +8 -6
  19. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/accessrules.py +8 -6
  20. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/actionengine.py +8 -6
  21. onvif_python-0.4.0/onvif/services/analytics/__init__.py +1 -0
  22. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/analytics/analytics.py +8 -6
  23. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/analytics/ruleengine.py +8 -6
  24. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/analyticsdevice.py +10 -8
  25. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/appmgmt.py +9 -7
  26. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/authenticationbehavior.py +9 -7
  27. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/credential.py +8 -6
  28. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/deviceio.py +18 -12
  29. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/devicemgmt.py +54 -49
  30. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/display.py +8 -6
  31. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/doorcontrol.py +8 -6
  32. onvif_python-0.4.0/onvif/services/events/__init__.py +1 -0
  33. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/events/events.py +8 -6
  34. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/events/notification.py +9 -24
  35. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/events/pausable_subscription.py +13 -9
  36. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/events/pullpoint.py +8 -6
  37. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/events/subscription.py +10 -9
  38. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/imaging.py +36 -32
  39. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/media.py +8 -6
  40. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/media2.py +9 -6
  41. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/provisioning.py +9 -7
  42. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/ptz.py +8 -6
  43. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/receiver.py +8 -6
  44. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/recording.py +8 -6
  45. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/replay.py +8 -6
  46. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/schedule.py +8 -6
  47. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/search.py +15 -6
  48. onvif_python-0.4.0/onvif/services/security/__init__.py +1 -0
  49. onvif_python-0.4.0/onvif/services/security/advancedsecurity.py +35 -0
  50. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/security/authorizationserver.py +9 -7
  51. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/security/dot1x.py +9 -7
  52. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/security/jwt.py +9 -7
  53. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/security/keystore.py +9 -7
  54. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/security/mediasigning.py +9 -7
  55. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/security/tlsserver.py +9 -7
  56. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/thermal.py +8 -6
  57. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/uplink.py +8 -6
  58. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/utils/__init__.py +9 -4
  59. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/utils/discovery.py +150 -51
  60. onvif_python-0.4.0/onvif/utils/error_handlers.py +237 -0
  61. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/utils/exceptions.py +64 -44
  62. onvif_python-0.4.0/onvif/utils/plugins.py +500 -0
  63. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/utils/service.py +106 -87
  64. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/utils/wsdl.py +101 -57
  65. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/utils/zeep.py +267 -286
  66. onvif_python-0.4.0/onvif_python.egg-info/PKG-INFO +113 -0
  67. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif_python.egg-info/SOURCES.txt +11 -2
  68. onvif_python-0.4.0/onvif_python.egg-info/requires.txt +19 -0
  69. {onvif_python-0.3.0 → onvif_python-0.4.0}/pyproject.toml +43 -6
  70. {onvif_python-0.3.0 → onvif_python-0.4.0}/tests/test_client.py +44 -60
  71. {onvif_python-0.3.0 → onvif_python-0.4.0}/tests/test_core.py +13 -34
  72. {onvif_python-0.3.0 → onvif_python-0.4.0}/tests/test_error_handlers.py +86 -70
  73. {onvif_python-0.3.0 → onvif_python-0.4.0}/tests/test_exceptions.py +16 -16
  74. onvif_python-0.3.0/PKG-INFO +0 -1219
  75. onvif_python-0.3.0/README.md +0 -1173
  76. onvif_python-0.3.0/onvif/cli/main.py +0 -989
  77. onvif_python-0.3.0/onvif/services/security/advancedsecurity.py +0 -33
  78. onvif_python-0.3.0/onvif/utils/error_handlers.py +0 -127
  79. onvif_python-0.3.0/onvif/utils/parser.py +0 -139
  80. onvif_python-0.3.0/onvif/utils/xml_capture.py +0 -222
  81. onvif_python-0.3.0/onvif_python.egg-info/PKG-INFO +0 -1219
  82. onvif_python-0.3.0/onvif_python.egg-info/requires.txt +0 -10
  83. {onvif_python-0.3.0 → onvif_python-0.4.0}/MANIFEST.in +0 -0
  84. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/cli/__init__.py +0 -0
  85. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/cli/__main__.py +0 -0
  86. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/db/companies.db +0 -0
  87. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/db/products.db +0 -0
  88. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/services/__init__.py +0 -0
  89. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/LICENSE.md +0 -0
  90. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/schema/common/b-2.xsd +0 -0
  91. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/schema/common/bf-2.xsd +0 -0
  92. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/schema/common/bw-2.wsdl +0 -0
  93. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/schema/common/include +0 -0
  94. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/schema/common/mpqf.xsd +0 -0
  95. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/schema/common/r-2.xsd +0 -0
  96. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/schema/common/rw-2.wsdl +0 -0
  97. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/schema/common/soap-envelope +0 -0
  98. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/schema/common/t-1.xsd +0 -0
  99. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/schema/common/ws-addr.xsd +0 -0
  100. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/schema/common/xml.xsd +0 -0
  101. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/schema/common/xmlmime +0 -0
  102. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
  103. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
  104. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
  105. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
  106. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
  107. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
  108. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
  109. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
  110. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
  111. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
  112. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
  113. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
  114. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
  115. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
  116. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
  117. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/display/display.wsdl +0 -0
  118. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
  119. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/display.wsdl +0 -0
  120. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
  121. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
  122. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
  123. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
  124. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
  125. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
  126. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
  127. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
  128. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
  129. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
  130. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
  131. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/receiver.wsdl +0 -0
  132. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/recording.wsdl +0 -0
  133. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/replay.wsdl +0 -0
  134. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
  135. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
  136. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/schema/common.xsd +0 -0
  137. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
  138. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
  139. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/search.wsdl +0 -0
  140. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
  141. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
  142. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
  143. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
  144. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
  145. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
  146. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
  147. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/analytics/index.html +0 -0
  148. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
  149. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
  150. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
  151. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
  152. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
  153. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
  154. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
  155. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
  156. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
  157. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
  158. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
  159. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
  160. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif_python.egg-info/dependency_links.txt +0 -0
  161. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif_python.egg-info/entry_points.txt +0 -0
  162. {onvif_python-0.3.0 → onvif_python-0.4.0}/onvif_python.egg-info/top_level.txt +0 -0
  163. {onvif_python-0.3.0 → onvif_python-0.4.0}/setup.cfg +0 -0
  164. {onvif_python-0.3.0 → onvif_python-0.4.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2025 Nirsimetri Technologies®
3
+ Copyright (c) 2025-2026 Nirsimetri Technologies®
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,113 @@
1
+ Metadata-Version: 2.4
2
+ Name: onvif-python
3
+ Version: 0.4.0
4
+ Summary: A modern Python library for ONVIF-compliant devices
5
+ Author-email: Nirsimetri Technologies® <open@nirsimetri.com>
6
+ Maintainer: Kaburagi
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/nirsimetri/onvif-python
9
+ Project-URL: Documentation, https://nirsimetri.github.io/onvif-python
10
+ Project-URL: Changelog, https://github.com/nirsimetri/onvif-python/releases
11
+ Keywords: onvif,Camera,onvif-python,ip-camera,cctv,soap,surveillance,cctv cameras,soap-client,onvif-client,onvif-library,onvif-specifications
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Environment :: Console
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Intended Audience :: Developers
21
+ Classifier: Intended Audience :: Information Technology
22
+ Classifier: Intended Audience :: Science/Research
23
+ Classifier: Topic :: Software Development :: Libraries
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Classifier: Topic :: Security
26
+ Classifier: Topic :: Multimedia :: Video
27
+ Classifier: Topic :: Multimedia :: Sound/Audio
28
+ Classifier: Topic :: Communications
29
+ Classifier: Topic :: Text Processing :: Markup :: XML
30
+ Classifier: Natural Language :: English
31
+ Classifier: Natural Language :: Indonesian
32
+ Requires-Python: >=3.10
33
+ Description-Content-Type: text/markdown
34
+ License-File: LICENSE.md
35
+ Requires-Dist: zeep>=4.3.0
36
+ Requires-Dist: requests>=2.32.0
37
+ Requires-Dist: pyreadline3>=3.5.4; sys_platform == "win32"
38
+ Provides-Extra: dev
39
+ Requires-Dist: pytest>=7.0; extra == "dev"
40
+ Requires-Dist: pytest-cov; extra == "dev"
41
+ Requires-Dist: black; extra == "dev"
42
+ Requires-Dist: mypy; extra == "dev"
43
+ Requires-Dist: isort; extra == "dev"
44
+ Requires-Dist: pylint; extra == "dev"
45
+ Requires-Dist: docformatter; extra == "dev"
46
+ Provides-Extra: docs
47
+ Requires-Dist: mkdocs-material; extra == "docs"
48
+ Requires-Dist: mkdocstrings[python]; extra == "docs"
49
+ Requires-Dist: mkdocs-git-authors-plugin; extra == "docs"
50
+ Dynamic: license-file
51
+
52
+ <h1 align="center">ONVIF Python</h1>
53
+
54
+ <div align="center">
55
+ <a href="https://app.codacy.com/gh/nirsimetri/onvif-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade"><img src="https://img.shields.io/codacy/grade/bff08a94e4d447b690cea49c6594826d?style=plastic&logo=codacy&label=Code%20Quality"/></a>
56
+ <a href="https://pypi.org/project/onvif-python/"><img alt="PyPI Version" src="https://img.shields.io/badge/Version-0.4.0-orange?logo=python&logoColor=white&color=yellow&style=plastic"></a>
57
+ <a href="https://pepy.tech/projects/onvif-python"><img alt="Pepy Total Downloads" src="https://img.shields.io/pepy/dt/onvif-python?logo=pypi&logoColor=white&label=Downloads&color=red&style=plastic"></a>
58
+ <br>
59
+ <a href="https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml"><img alt="Build" src="https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml/badge.svg?branch=main"></a>
60
+ <a href="https://github.com/nirsimetri/onvif-python/actions/workflows/python-publish.yml"><img alt="Upload Python Package" src="https://github.com/nirsimetri/onvif-python/actions/workflows/python-publish.yml/badge.svg"></a>
61
+ </div>
62
+
63
+ <h1 align="center">
64
+ <img src="https://raw.githubusercontent.com/nirsimetri/onvif-python/refs/heads/main/assets/images/carbon_onvif.png" alt="onvif" width="700px">
65
+ <br>
66
+ </h1>
67
+
68
+ **This project provides a comprehensive and developer-friendly Python library for working with ONVIF-compliant devices.** It is designed to be reliable, easy to integrate, and flexible enough to support a wide range of ONVIF profiles and services.
69
+
70
+ **[ONVIF](https://www.onvif.org) (Open Network Video Interface Forum)** is a global standard for the interface of IP-based physical security products, including network cameras, video recorders, and related systems.
71
+
72
+ Behind the scenes, ONVIF communication relies on **[SOAP](https://en.wikipedia.org/wiki/SOAP) (Simple Object Access Protocol)**; an [XML](https://en.wikipedia.org/wiki/XML)-based messaging protocol with strict schema definitions ([WSDL](https://en.wikipedia.org/wiki/Web_Services_Description_Language)/[XSD](https://en.wikipedia.org/wiki/XML_Schema_(W3C))). SOAP ensures interoperability, but when used directly it can be verbose, complex, and error-prone.
73
+
74
+ This library simplifies that process by wrapping SOAP communication into a clean, Pythonic API. You no longer need to handle low-level XML parsing, namespaces, or security tokens manually; the library takes care of it, letting you focus on building functionality.
75
+
76
+ ## Who Is It For?
77
+ - **Individual developers** exploring ONVIF or building hobby projects
78
+ - **Companies** building video intelligence, analytics, or VMS platforms
79
+ - **Security integrators** who need reliable ONVIF interoperability across devices
80
+
81
+ ## Requirements
82
+
83
+ - **Python**: 3.10 or higher
84
+ - **Dependencies**:
85
+ - [`zeep>=4.3.0`](https://github.com/mvantellingen/python-zeep) - SOAP client for ONVIF communication
86
+ - [`requests>=2.32.0`](https://github.com/psf/requests) - HTTP library for network requests
87
+
88
+ ## Installation
89
+
90
+ From official [PyPI](https://pypi.org/project/onvif-python/):
91
+ ```shell
92
+ pip install --upgrade onvif-python
93
+ ```
94
+
95
+ Or clone this repository and install locally:
96
+ ```shell
97
+ git clone https://github.com/nirsimetri/onvif-python
98
+ cd onvif-python
99
+ pip install .
100
+ ```
101
+
102
+ Once the installation is successful, you can also immediately access the [ONVIF CLI](https://nirsimetri.github.io/onvif-python/core/onvif_cli/) via the terminal using:
103
+ ```shell
104
+ onvif --help
105
+ ```
106
+
107
+ ## Documentation
108
+
109
+ For complete guide, including usage examples and API references, please visit the [Documentation Page](https://nirsimetri.github.io/onvif-python/).
110
+
111
+ ## License
112
+
113
+ This project is licensed under the MIT License. See [LICENSE](./LICENSE.md) for details.
@@ -0,0 +1,62 @@
1
+ <h1 align="center">ONVIF Python</h1>
2
+
3
+ <div align="center">
4
+ <a href="https://app.codacy.com/gh/nirsimetri/onvif-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade"><img src="https://img.shields.io/codacy/grade/bff08a94e4d447b690cea49c6594826d?style=plastic&logo=codacy&label=Code%20Quality"/></a>
5
+ <a href="https://pypi.org/project/onvif-python/"><img alt="PyPI Version" src="https://img.shields.io/badge/Version-0.4.0-orange?logo=python&logoColor=white&color=yellow&style=plastic"></a>
6
+ <a href="https://pepy.tech/projects/onvif-python"><img alt="Pepy Total Downloads" src="https://img.shields.io/pepy/dt/onvif-python?logo=pypi&logoColor=white&label=Downloads&color=red&style=plastic"></a>
7
+ <br>
8
+ <a href="https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml"><img alt="Build" src="https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml/badge.svg?branch=main"></a>
9
+ <a href="https://github.com/nirsimetri/onvif-python/actions/workflows/python-publish.yml"><img alt="Upload Python Package" src="https://github.com/nirsimetri/onvif-python/actions/workflows/python-publish.yml/badge.svg"></a>
10
+ </div>
11
+
12
+ <h1 align="center">
13
+ <img src="https://raw.githubusercontent.com/nirsimetri/onvif-python/refs/heads/main/assets/images/carbon_onvif.png" alt="onvif" width="700px">
14
+ <br>
15
+ </h1>
16
+
17
+ **This project provides a comprehensive and developer-friendly Python library for working with ONVIF-compliant devices.** It is designed to be reliable, easy to integrate, and flexible enough to support a wide range of ONVIF profiles and services.
18
+
19
+ **[ONVIF](https://www.onvif.org) (Open Network Video Interface Forum)** is a global standard for the interface of IP-based physical security products, including network cameras, video recorders, and related systems.
20
+
21
+ Behind the scenes, ONVIF communication relies on **[SOAP](https://en.wikipedia.org/wiki/SOAP) (Simple Object Access Protocol)**; an [XML](https://en.wikipedia.org/wiki/XML)-based messaging protocol with strict schema definitions ([WSDL](https://en.wikipedia.org/wiki/Web_Services_Description_Language)/[XSD](https://en.wikipedia.org/wiki/XML_Schema_(W3C))). SOAP ensures interoperability, but when used directly it can be verbose, complex, and error-prone.
22
+
23
+ This library simplifies that process by wrapping SOAP communication into a clean, Pythonic API. You no longer need to handle low-level XML parsing, namespaces, or security tokens manually; the library takes care of it, letting you focus on building functionality.
24
+
25
+ ## Who Is It For?
26
+ - **Individual developers** exploring ONVIF or building hobby projects
27
+ - **Companies** building video intelligence, analytics, or VMS platforms
28
+ - **Security integrators** who need reliable ONVIF interoperability across devices
29
+
30
+ ## Requirements
31
+
32
+ - **Python**: 3.10 or higher
33
+ - **Dependencies**:
34
+ - [`zeep>=4.3.0`](https://github.com/mvantellingen/python-zeep) - SOAP client for ONVIF communication
35
+ - [`requests>=2.32.0`](https://github.com/psf/requests) - HTTP library for network requests
36
+
37
+ ## Installation
38
+
39
+ From official [PyPI](https://pypi.org/project/onvif-python/):
40
+ ```shell
41
+ pip install --upgrade onvif-python
42
+ ```
43
+
44
+ Or clone this repository and install locally:
45
+ ```shell
46
+ git clone https://github.com/nirsimetri/onvif-python
47
+ cd onvif-python
48
+ pip install .
49
+ ```
50
+
51
+ Once the installation is successful, you can also immediately access the [ONVIF CLI](https://nirsimetri.github.io/onvif-python/core/onvif_cli/) via the terminal using:
52
+ ```shell
53
+ onvif --help
54
+ ```
55
+
56
+ ## Documentation
57
+
58
+ For complete guide, including usage examples and API references, please visit the [Documentation Page](https://nirsimetri.github.io/onvif-python/).
59
+
60
+ ## License
61
+
62
+ This project is licensed under the MIT License. See [LICENSE](./LICENSE.md) for details.
@@ -1,4 +1,4 @@
1
- """ONVIF Python"""
1
+ """ONVIF Python."""
2
2
 
3
3
  from onvif.cli import main as ONVIFCLI
4
4
  from onvif.client import ONVIFClient
@@ -6,10 +6,12 @@ from onvif.operator import CacheMode
6
6
  from onvif.utils import (
7
7
  ONVIFWSDL,
8
8
  ONVIFDiscovery,
9
- ONVIFErrorHandler,
10
9
  ONVIFOperationException,
11
10
  ONVIFParser,
12
11
  ZeepPatcher,
12
+ ignore_unsupported,
13
+ is_action_not_supported,
14
+ safe_call,
13
15
  )
14
16
 
15
17
  __all__ = [
@@ -17,9 +19,11 @@ __all__ = [
17
19
  "CacheMode",
18
20
  "ONVIFWSDL",
19
21
  "ONVIFOperationException",
20
- "ONVIFErrorHandler",
21
22
  "ZeepPatcher",
22
23
  "ONVIFCLI",
23
24
  "ONVIFDiscovery",
24
25
  "ONVIFParser",
26
+ "ignore_unsupported",
27
+ "is_action_not_supported",
28
+ "safe_call",
25
29
  ]
@@ -0,0 +1,21 @@
1
+ """ONVIF CLI helpers."""
2
+
3
+ from onvif.cli.helpers.discovery import discover_devices, select_device_interactive
4
+ from onvif.cli.helpers.execution import execute_command, setup_warning_format
5
+ from onvif.cli.helpers.files import save_output_to_file
6
+ from onvif.cli.helpers.products import search_products
7
+ from onvif.cli.helpers.wsdl_parser import (
8
+ get_method_documentation,
9
+ get_operation_type_info,
10
+ )
11
+
12
+ __all__ = [
13
+ "search_products",
14
+ "save_output_to_file",
15
+ "discover_devices",
16
+ "select_device_interactive",
17
+ "execute_command",
18
+ "setup_warning_format",
19
+ "get_method_documentation",
20
+ "get_operation_type_info",
21
+ ]
@@ -0,0 +1,156 @@
1
+ """ONVIF discovery helpers."""
2
+
3
+ from datetime import datetime
4
+
5
+ from onvif.cli.utils import colorize
6
+ from onvif.utils import ONVIFDiscovery
7
+
8
+
9
+ def discover_devices(
10
+ timeout: int = 4,
11
+ interface: str | None = None,
12
+ prefer_https: bool = False,
13
+ filter_term: str | None = None,
14
+ ) -> list:
15
+ """Discover ONVIF devices on the network using WS-Discovery.
16
+
17
+ Args:
18
+ timeout (int): Discovery timeout in seconds
19
+ interface (str | None): Network interface to use for discovery
20
+ prefer_https (bool): If True, prioritize HTTPS XAddrs when available
21
+ filter_term (str | None): Optional search term to filter devices by types or scopes (case-insensitive)
22
+
23
+ Returns:
24
+ List of discovered devices with connection info
25
+ """
26
+ # Use ONVIFDiscovery class
27
+ discovery = ONVIFDiscovery(timeout=timeout, interface=interface)
28
+
29
+ print(f"\n{colorize('Discovering ONVIF devices on network...', 'yellow')}")
30
+ print(f"Network interface: {colorize(discovery.get_local_ip(), 'white')}")
31
+ print(f"Timeout: {timeout}s")
32
+ if filter_term:
33
+ print(f"Filter: {colorize(filter_term, 'yellow')}")
34
+ print()
35
+
36
+ devices = discovery.discover(prefer_https=prefer_https, search=filter_term)
37
+
38
+ return devices
39
+
40
+
41
+ # pylint: disable=too-many-locals,too-many-statements,too-many-branches
42
+ def select_device_interactive(devices: list) -> tuple[str, int, bool] | None:
43
+ """Display devices and allow user to select one interactively.
44
+
45
+ Args:
46
+ devices (list): Discovered devices
47
+
48
+ Returns:
49
+ Tuple of (host, port, use_https) or None if cancelled
50
+ """
51
+ if not devices:
52
+ print(f"\n{colorize('No ONVIF devices found.', 'red')}")
53
+ return None
54
+
55
+ print(f"{colorize(f'Found {len(devices)} ONVIF device(s):', 'green')}")
56
+
57
+ for idx, device in enumerate(devices, 1):
58
+ idx_str = colorize(f"[{idx}]", "yellow")
59
+ protocol = "https" if device.get("use_https", False) else "http"
60
+ host_port = f"{device['host']}:{device['port']}"
61
+ protocol_indicator = (
62
+ colorize("🔒 HTTPS", "green")
63
+ if device.get("use_https", False)
64
+ else colorize("HTTP", "white")
65
+ )
66
+ print(f"\n{idx_str} {colorize(host_port, 'yellow')} ({protocol_indicator})")
67
+
68
+ # Remove uuid: or urn:uuid: prefix from EPR
69
+ epr_display = device["epr"]
70
+ if epr_display.startswith("urn:uuid:"):
71
+ epr_display = epr_display.replace("urn:uuid:", "")
72
+ elif epr_display.startswith("uuid:"):
73
+ epr_display = epr_display.replace("uuid:", "")
74
+ print(f" [uuid] {epr_display}")
75
+
76
+ if device.get("hostname"):
77
+ print(f" [hostname] {device["hostname"]}")
78
+
79
+ if device.get("xaddrs"):
80
+ xaddrs_parts = [f"[{xaddr}]" for xaddr in device["xaddrs"]]
81
+ print(f" [xaddrs] {' '.join(xaddrs_parts)}")
82
+
83
+ if device.get("date_time"):
84
+ utc = (
85
+ datetime.fromisoformat(device["date_time"].get("utc")).strftime(
86
+ "%H:%M:%S %d-%m-%Y"
87
+ )
88
+ if device["date_time"].get("utc")
89
+ else ""
90
+ )
91
+ local = (
92
+ datetime.fromisoformat(device["date_time"].get("local")).strftime(
93
+ "%H:%M:%S %d-%m-%Y"
94
+ )
95
+ if device["date_time"].get("local")
96
+ else ""
97
+ )
98
+ print(
99
+ f" [date_time]"
100
+ f'{" [UTC: " + utc + "]" if utc else ""}'
101
+ f'{" [Local: " + local + "]" if local else ""}'
102
+ )
103
+
104
+ if device.get("types"):
105
+ types_parts = [f"[{t}]" for t in device["types"]]
106
+ print(f" [types] {' '.join(types_parts)}")
107
+
108
+ if device.get("services"):
109
+ service = [colorize(f"[{t}]", "cyan") for t in device["services"]]
110
+ print(f" [services] {' '.join(service)}")
111
+
112
+ if device.get("scopes"):
113
+ scope_parts = []
114
+ for scope in device["scopes"]:
115
+ # Remove the prefix "onvif://www.onvif.org/" if present
116
+ if scope.startswith("onvif://www.onvif.org/"):
117
+ simplified = scope.replace("onvif://www.onvif.org/", "")
118
+ scope_parts.append(f"[{simplified}]")
119
+ else:
120
+ # Keep other scopes as-is (e.g., http:123)
121
+ scope_parts.append(f"[{scope}]")
122
+
123
+ if scope_parts:
124
+ print(f" [scopes] {' '.join(scope_parts)}")
125
+
126
+ # Simple selection (without arrow keys for cross-platform compatibility)
127
+ while True:
128
+ try:
129
+ selection = input(
130
+ f"\nSelect device number {colorize(f'1-{len(devices)}', 'white')} or {colorize('q', 'white')} to quit: "
131
+ )
132
+
133
+ if selection.lower() == "q":
134
+ return None
135
+
136
+ idx = int(selection)
137
+ if 1 <= idx <= len(devices):
138
+ selected = devices[idx - 1]
139
+ protocol = "https" if selected.get("use_https", False) else "http"
140
+ host_port = f"{selected['host']}:{selected['port']}"
141
+ print(
142
+ f"\n{colorize('Selected:', 'green')} {colorize(protocol, 'cyan')}://{colorize(host_port, 'yellow')}"
143
+ )
144
+
145
+ return (
146
+ selected["host"],
147
+ selected["port"],
148
+ selected.get("use_https", False),
149
+ )
150
+
151
+ print(colorize("Invalid selection. Please try again.", "red"))
152
+
153
+ except ValueError:
154
+ print(colorize("Invalid input. Please enter a number.", "red"))
155
+ except (EOFError, KeyboardInterrupt):
156
+ return None
@@ -0,0 +1,57 @@
1
+ """ONVIF command execution helpers."""
2
+
3
+ import warnings
4
+ from typing import Any
5
+
6
+ from onvif.cli.utils import colorize, parse_json_params
7
+ from onvif.client import ONVIFClient
8
+
9
+
10
+ def execute_command(
11
+ client: ONVIFClient,
12
+ service_name: str,
13
+ method_name: str,
14
+ params_str: str | None = None,
15
+ ) -> Any:
16
+ """Execute a single ONVIF command.
17
+
18
+ Args:
19
+ client (ONVIFClient): ONVIFClient instance
20
+ service_name (str): Selected service for command execution
21
+ method_name (str): Method to execute by service
22
+ params_str (str | None): Provided params for method execution
23
+
24
+ Returns:
25
+ The operation response printed in terminal
26
+ """
27
+ # Get service instance
28
+ try:
29
+ service = getattr(client, service_name.lower())()
30
+ except AttributeError as e:
31
+ raise ValueError(f"{colorize('Unknown service:', 'red')} {service_name}") from e
32
+
33
+ # Get method
34
+ try:
35
+ method = getattr(service, method_name)
36
+ except AttributeError as e:
37
+ raise ValueError(
38
+ f"{colorize('Unknown method', 'red')} '{method_name}' for service '{service_name}'"
39
+ ) from e
40
+
41
+ # Parse parameters
42
+ params = parse_json_params(params_str) if params_str else {}
43
+
44
+ # Execute method
45
+ return method(**params)
46
+
47
+
48
+ def setup_warning_format() -> None:
49
+ """Setup custom warning format to show clean, concise warnings."""
50
+
51
+ def custom_warning_format(
52
+ message, category, filename, lineno, line=None
53
+ ): # pylint: disable=unused-argument
54
+ # Show only the warning message without file path and line number
55
+ return f"{category.__name__}: {message}\n"
56
+
57
+ warnings.formatwarning = custom_warning_format
@@ -0,0 +1,191 @@
1
+ """File processing helpers."""
2
+
3
+ import json
4
+ import os
5
+ import sys
6
+ from datetime import datetime, timezone
7
+ from typing import Any
8
+
9
+ from onvif.cli.utils import colorize
10
+ from onvif.client import ONVIFClient
11
+
12
+
13
+ def save_output_to_file(
14
+ result: Any, output_path: str, debug_mode: bool, client: ONVIFClient
15
+ ) -> None:
16
+ """Save command output to file in appropriate format based on file extension.
17
+
18
+ Args:
19
+ result (Any): The ONVIF command result
20
+ output_path (str): Path to output file
21
+ debug_mode (bool): Whether debug mode is enabled (for XML capture)
22
+ client (ONVIFClient): ONVIFClient instance (for accessing XML plugin)
23
+ """
24
+ try:
25
+ # Determine output format based on file extension
26
+ _, ext = os.path.splitext(output_path.lower())
27
+
28
+ if ext == ".json":
29
+ # Prepare output data
30
+ output_data = {}
31
+
32
+ # JSON format
33
+ output_data["result"] = _serialize_for_json(result)
34
+ output_data["timestamp"] = datetime.now(timezone.utc).isoformat()
35
+ output_data["raw_result"] = str(result) # Add raw string as fallback
36
+
37
+ # Add XML data if debug mode is enabled and XML plugin is available
38
+ if debug_mode and client.xml_plugin:
39
+ output_data["debug"] = {
40
+ "last_request_xml": client.xml_plugin.last_sent_xml,
41
+ "last_response_xml": client.xml_plugin.last_received_xml,
42
+ "last_operation": client.xml_plugin.last_operation,
43
+ }
44
+
45
+ with open(output_path, "w", encoding="utf-8") as f:
46
+ json.dump(output_data, f, indent=2, ensure_ascii=False)
47
+
48
+ elif ext == ".xml":
49
+ # XML format - prioritize raw SOAP XML over parsed result
50
+ if client.xml_plugin and client.xml_plugin.last_received_xml:
51
+ # Save the raw SOAP response XML with minimal wrapper
52
+ content = f"""<?xml version="1.0" encoding="UTF-8"?>
53
+ <!-- ONVIF SOAP Response -->
54
+ <!-- Timestamp: {datetime.now(timezone.utc).isoformat()} -->
55
+ <!-- Operation: {client.xml_plugin.last_operation or 'Unknown'} -->
56
+
57
+ {client.xml_plugin.last_received_xml}
58
+ """
59
+ else:
60
+ # Fallback: Simple XML wrapper for the parsed result
61
+ content = f"""<?xml version="1.0" encoding="UTF-8"?>
62
+ <!-- ONVIF Command Output (Parsed Result) -->
63
+ <!-- Timestamp: {datetime.now(timezone.utc).isoformat()} -->
64
+ <!-- Note: Raw SOAP XML not available. Enable --debug for full SOAP capture. -->
65
+ <onvif_result>
66
+ <![CDATA[
67
+ {result!s}
68
+ ]]>
69
+ </onvif_result>
70
+ """
71
+
72
+ with open(output_path, "w", encoding="utf-8") as f:
73
+ f.write(content)
74
+
75
+ else:
76
+ # Plain text format (default)
77
+ content = "ONVIF Command Output\n"
78
+ content += f"Timestamp: {datetime.now(timezone.utc).isoformat()}\n"
79
+ content += f"{'='*50}\n\n"
80
+ content += str(result)
81
+
82
+ # Add debug information if available
83
+ if debug_mode and client.xml_plugin:
84
+ content += f"\n\n{'='*50}\n"
85
+ content += "DEBUG INFORMATION\n"
86
+ content += f"{'='*50}\n"
87
+ if client.xml_plugin.last_operation:
88
+ content += f"Operation: {client.xml_plugin.last_operation}\n\n"
89
+ if client.xml_plugin.last_sent_xml:
90
+ content += "SOAP Request:\n"
91
+ content += client.xml_plugin.last_sent_xml + "\n\n"
92
+ if client.xml_plugin.last_received_xml:
93
+ content += "SOAP Response:\n"
94
+ content += client.xml_plugin.last_received_xml + "\n"
95
+
96
+ with open(output_path, "w", encoding="utf-8") as f:
97
+ f.write(content)
98
+
99
+ except (AttributeError, ValueError, OSError) as e:
100
+ print(f"{colorize('Error saving output:', 'red')} {e}", file=sys.stderr)
101
+ # Still print the result to console if file save fails
102
+ print(str(result))
103
+
104
+
105
+ # pylint: disable=too-many-return-statements,too-many-branches,too-many-nested-blocks
106
+ def _serialize_for_json(obj: Any) -> Any:
107
+ """Recursively serialize ONVIF objects for JSON output.
108
+
109
+ Args:
110
+ obj: Object to serialize
111
+
112
+ Returns:
113
+ JSON-serializable representation of the object
114
+ """
115
+ if obj is None:
116
+ return None
117
+ if isinstance(obj, (str, int, float, bool)):
118
+ return obj
119
+ if isinstance(obj, datetime):
120
+ return obj.isoformat()
121
+ if isinstance(obj, (list, tuple)):
122
+ return [_serialize_for_json(item) for item in obj]
123
+ if isinstance(obj, dict):
124
+ return {key: _serialize_for_json(value) for key, value in obj.items()}
125
+
126
+ # Check if this is a Zeep object (has _xsd_type attribute)
127
+ if hasattr(obj, "_xsd_type"):
128
+ result = {}
129
+ # Try to get all elements from XSD type
130
+ if hasattr(obj._xsd_type, "elements"): # pylint: disable=protected-access
131
+ for (
132
+ elem_name,
133
+ _,
134
+ ) in obj._xsd_type.elements: # pylint: disable=protected-access
135
+ try:
136
+ value = getattr(obj, elem_name, None)
137
+ if value is not None:
138
+ result[elem_name] = _serialize_for_json(value)
139
+ except (AttributeError, TypeError):
140
+ # Skip elements that can't be accessed or have type issues
141
+ pass
142
+
143
+ # Also try regular attributes
144
+ for attr_name in dir(obj):
145
+ if not attr_name.startswith("_") and not callable(
146
+ getattr(obj, attr_name, None)
147
+ ):
148
+ try:
149
+ attr_value = getattr(obj, attr_name)
150
+ if attr_value is not None and attr_name not in result:
151
+ result[attr_name] = _serialize_for_json(attr_value)
152
+ except (AttributeError, TypeError):
153
+ # Skip attributes that can't be accessed or have type issues
154
+ pass
155
+
156
+ return result
157
+
158
+ if hasattr(obj, "__dict__"):
159
+ # Handle regular objects with attributes
160
+ result = {}
161
+ for key, value in obj.__dict__.items():
162
+ if not key.startswith("_"): # Skip private attributes
163
+ result[key] = _serialize_for_json(value)
164
+
165
+ # If result is empty, try to get attributes using dir()
166
+ if not result:
167
+ for attr_name in dir(obj):
168
+ if not attr_name.startswith("_") and not callable(
169
+ getattr(obj, attr_name, None)
170
+ ):
171
+ try:
172
+ attr_value = getattr(obj, attr_name)
173
+ if attr_value is not None:
174
+ result[attr_name] = _serialize_for_json(attr_value)
175
+ except (AttributeError, TypeError):
176
+ # Skip attributes that can't be accessed or have type issues
177
+ pass
178
+
179
+ return result
180
+
181
+ if hasattr(obj, "_value_1"):
182
+ # Handle zeep objects with special structure
183
+ return _serialize_for_json(obj._value_1) # pylint: disable=protected-access
184
+
185
+ # Try to convert to dict using vars() if available
186
+ try:
187
+ obj_dict = vars(obj)
188
+ return _serialize_for_json(obj_dict)
189
+ except TypeError:
190
+ # Fallback to string representation
191
+ return str(obj)