onvif-python 0.2.11__tar.gz → 0.3.1__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 (200) hide show
  1. {onvif_python-0.2.11 → onvif_python-0.3.1}/LICENSE.md +1 -1
  2. onvif_python-0.3.1/PKG-INFO +103 -0
  3. onvif_python-0.3.1/README.md +56 -0
  4. onvif_python-0.3.1/onvif/__init__.py +25 -0
  5. onvif_python-0.3.1/onvif/cli/__init__.py +5 -0
  6. onvif_python-0.3.1/onvif/cli/__main__.py +7 -0
  7. onvif_python-0.3.1/onvif/cli/helpers/__init__.py +21 -0
  8. onvif_python-0.3.1/onvif/cli/helpers/discovery.py +124 -0
  9. onvif_python-0.3.1/onvif/cli/helpers/execution.py +57 -0
  10. onvif_python-0.3.1/onvif/cli/helpers/files.py +189 -0
  11. onvif_python-0.3.1/onvif/cli/helpers/messages.py +221 -0
  12. onvif_python-0.3.1/onvif/cli/helpers/products.py +292 -0
  13. onvif_python-0.2.11/onvif/cli/utils.py → onvif_python-0.3.1/onvif/cli/helpers/wsdl_parser.py +132 -707
  14. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/cli/interactive.py +97 -243
  15. onvif_python-0.3.1/onvif/cli/main.py +336 -0
  16. onvif_python-0.3.1/onvif/cli/utils.py +508 -0
  17. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/client.py +292 -207
  18. onvif_python-0.3.1/onvif/meta.py +6 -0
  19. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/operator.py +107 -49
  20. onvif_python-0.3.1/onvif/services/__init__.py +79 -0
  21. onvif_python-0.3.1/onvif/services/accesscontrol.py +291 -0
  22. onvif_python-0.3.1/onvif/services/accessrules.py +127 -0
  23. onvif_python-0.3.1/onvif/services/actionengine.py +108 -0
  24. onvif_python-0.3.1/onvif/services/analytics/__init__.py +0 -0
  25. onvif_python-0.3.1/onvif/services/analytics/analytics.py +123 -0
  26. onvif_python-0.3.1/onvif/services/analytics/ruleengine.py +77 -0
  27. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/services/analyticsdevice.py +58 -23
  28. onvif_python-0.3.1/onvif/services/appmgmt.py +73 -0
  29. onvif_python-0.3.1/onvif/services/authenticationbehavior.py +218 -0
  30. onvif_python-0.3.1/onvif/services/credential.py +320 -0
  31. onvif_python-0.3.1/onvif/services/deviceio.py +326 -0
  32. onvif_python-0.3.1/onvif/services/devicemgmt.py +966 -0
  33. onvif_python-0.3.1/onvif/services/display.py +131 -0
  34. onvif_python-0.3.1/onvif/services/doorcontrol.py +266 -0
  35. onvif_python-0.3.1/onvif/services/events/__init__.py +0 -0
  36. onvif_python-0.3.1/onvif/services/events/events.py +86 -0
  37. onvif_python-0.3.1/onvif/services/events/notification.py +71 -0
  38. onvif_python-0.3.1/onvif/services/events/pausable_subscription.py +72 -0
  39. onvif_python-0.3.1/onvif/services/events/pullpoint.py +79 -0
  40. onvif_python-0.3.1/onvif/services/events/subscription.py +52 -0
  41. onvif_python-0.3.1/onvif/services/imaging.py +142 -0
  42. onvif_python-0.3.1/onvif/services/media.py +885 -0
  43. onvif_python-0.3.1/onvif/services/media2.py +665 -0
  44. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/services/provisioning.py +22 -8
  45. onvif_python-0.3.1/onvif/services/ptz.py +379 -0
  46. onvif_python-0.3.1/onvif/services/receiver.py +93 -0
  47. onvif_python-0.3.1/onvif/services/recording.py +288 -0
  48. onvif_python-0.3.1/onvif/services/replay.py +59 -0
  49. onvif_python-0.3.1/onvif/services/schedule.py +181 -0
  50. onvif_python-0.3.1/onvif/services/search.py +402 -0
  51. onvif_python-0.3.1/onvif/services/security/__init__.py +0 -0
  52. onvif_python-0.3.1/onvif/services/security/advancedsecurity.py +34 -0
  53. onvif_python-0.3.1/onvif/services/security/authorizationserver.py +52 -0
  54. onvif_python-0.3.1/onvif/services/security/dot1x.py +160 -0
  55. onvif_python-0.3.1/onvif/services/security/jwt.py +36 -0
  56. onvif_python-0.3.1/onvif/services/security/keystore.py +611 -0
  57. onvif_python-0.3.1/onvif/services/security/mediasigning.py +77 -0
  58. onvif_python-0.3.1/onvif/services/security/tlsserver.py +261 -0
  59. onvif_python-0.3.1/onvif/services/thermal.py +87 -0
  60. onvif_python-0.3.1/onvif/services/uplink.py +55 -0
  61. onvif_python-0.3.1/onvif/utils/__init__.py +21 -0
  62. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/utils/discovery.py +30 -44
  63. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/utils/error_handlers.py +30 -21
  64. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/utils/exceptions.py +34 -28
  65. onvif_python-0.2.11/onvif/utils/parser.py → onvif_python-0.3.1/onvif/utils/plugins.py +41 -8
  66. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/utils/service.py +11 -14
  67. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/utils/wsdl.py +52 -36
  68. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/utils/xml_capture.py +15 -18
  69. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/utils/zeep.py +13 -19
  70. onvif_python-0.3.1/onvif_python.egg-info/PKG-INFO +103 -0
  71. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif_python.egg-info/SOURCES.txt +12 -1
  72. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif_python.egg-info/requires.txt +4 -1
  73. {onvif_python-0.2.11 → onvif_python-0.3.1}/pyproject.toml +35 -2
  74. {onvif_python-0.2.11 → onvif_python-0.3.1}/setup.py +2 -0
  75. {onvif_python-0.2.11 → onvif_python-0.3.1}/tests/test_client.py +24 -24
  76. {onvif_python-0.2.11 → onvif_python-0.3.1}/tests/test_core.py +63 -34
  77. {onvif_python-0.2.11 → onvif_python-0.3.1}/tests/test_error_handlers.py +22 -22
  78. {onvif_python-0.2.11 → onvif_python-0.3.1}/tests/test_exceptions.py +21 -21
  79. onvif_python-0.2.11/PKG-INFO +0 -1219
  80. onvif_python-0.2.11/README.md +0 -1173
  81. onvif_python-0.2.11/onvif/__init__.py +0 -33
  82. onvif_python-0.2.11/onvif/cli/__init__.py +0 -5
  83. onvif_python-0.2.11/onvif/cli/__main__.py +0 -7
  84. onvif_python-0.2.11/onvif/cli/main.py +0 -977
  85. onvif_python-0.2.11/onvif/services/__init__.py +0 -79
  86. onvif_python-0.2.11/onvif/services/accesscontrol.py +0 -121
  87. onvif_python-0.2.11/onvif/services/accessrules.py +0 -52
  88. onvif_python-0.2.11/onvif/services/actionengine.py +0 -51
  89. onvif_python-0.2.11/onvif/services/analytics/analytics.py +0 -65
  90. onvif_python-0.2.11/onvif/services/analytics/ruleengine.py +0 -49
  91. onvif_python-0.2.11/onvif/services/appmgmt.py +0 -45
  92. onvif_python-0.2.11/onvif/services/authenticationbehavior.py +0 -88
  93. onvif_python-0.2.11/onvif/services/credential.py +0 -164
  94. onvif_python-0.2.11/onvif/services/deviceio.py +0 -170
  95. onvif_python-0.2.11/onvif/services/devicemgmt.py +0 -425
  96. onvif_python-0.2.11/onvif/services/display.py +0 -67
  97. onvif_python-0.2.11/onvif/services/doorcontrol.py +0 -98
  98. onvif_python-0.2.11/onvif/services/events/events.py +0 -46
  99. onvif_python-0.2.11/onvif/services/events/notification.py +0 -38
  100. onvif_python-0.2.11/onvif/services/events/pausable_subscription.py +0 -31
  101. onvif_python-0.2.11/onvif/services/events/pullpoint.py +0 -33
  102. onvif_python-0.2.11/onvif/services/events/subscription.py +0 -25
  103. onvif_python-0.2.11/onvif/services/imaging.py +0 -69
  104. onvif_python-0.2.11/onvif/services/media.py +0 -429
  105. onvif_python-0.2.11/onvif/services/media2.py +0 -340
  106. onvif_python-0.2.11/onvif/services/ptz.py +0 -192
  107. onvif_python-0.2.11/onvif/services/receiver.py +0 -51
  108. onvif_python-0.2.11/onvif/services/recording.py +0 -171
  109. onvif_python-0.2.11/onvif/services/replay.py +0 -35
  110. onvif_python-0.2.11/onvif/services/schedule.py +0 -87
  111. onvif_python-0.2.11/onvif/services/search.py +0 -218
  112. onvif_python-0.2.11/onvif/services/security/advancedsecurity.py +0 -24
  113. onvif_python-0.2.11/onvif/services/security/authorizationserver.py +0 -36
  114. onvif_python-0.2.11/onvif/services/security/dot1x.py +0 -47
  115. onvif_python-0.2.11/onvif/services/security/jwt.py +0 -26
  116. onvif_python-0.2.11/onvif/services/security/keystore.py +0 -213
  117. onvif_python-0.2.11/onvif/services/security/mediasigning.py +0 -35
  118. onvif_python-0.2.11/onvif/services/security/tlsserver.py +0 -87
  119. onvif_python-0.2.11/onvif/services/thermal.py +0 -59
  120. onvif_python-0.2.11/onvif/services/uplink.py +0 -33
  121. onvif_python-0.2.11/onvif/utils/__init__.py +0 -21
  122. onvif_python-0.2.11/onvif_python.egg-info/PKG-INFO +0 -1219
  123. {onvif_python-0.2.11 → onvif_python-0.3.1}/MANIFEST.in +0 -0
  124. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/db/companies.db +0 -0
  125. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/db/products.db +0 -0
  126. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/LICENSE.md +0 -0
  127. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/schema/common/b-2.xsd +0 -0
  128. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/schema/common/bf-2.xsd +0 -0
  129. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/schema/common/bw-2.wsdl +0 -0
  130. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/schema/common/include +0 -0
  131. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/schema/common/mpqf.xsd +0 -0
  132. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/schema/common/r-2.xsd +0 -0
  133. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/schema/common/rw-2.wsdl +0 -0
  134. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/schema/common/soap-envelope +0 -0
  135. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/schema/common/t-1.xsd +0 -0
  136. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/schema/common/ws-addr.xsd +0 -0
  137. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/schema/common/xml.xsd +0 -0
  138. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/schema/common/xmlmime +0 -0
  139. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -0
  140. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -0
  141. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -0
  142. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/actionengine.wsdl +0 -0
  143. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -0
  144. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -0
  145. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -0
  146. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -0
  147. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -0
  148. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -0
  149. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -0
  150. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -0
  151. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/credential/wsdl/index.html +0 -0
  152. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -0
  153. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/deviceio.wsdl +0 -0
  154. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/display/display.wsdl +0 -0
  155. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/display/wsdl/index.htm +0 -0
  156. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/display.wsdl +0 -0
  157. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -0
  158. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -0
  159. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -0
  160. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -0
  161. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/events/wsdl/index.html +0 -0
  162. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/federatedsearch.wsdl +0 -0
  163. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -0
  164. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -0
  165. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -0
  166. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/pacs/types.xsd +0 -0
  167. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -0
  168. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/receiver.wsdl +0 -0
  169. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/recording.wsdl +0 -0
  170. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/replay.wsdl +0 -0
  171. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -0
  172. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -0
  173. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/schema/common.xsd +0 -0
  174. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -0
  175. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/schema/onvif.xsd +0 -0
  176. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/search.wsdl +0 -0
  177. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -0
  178. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -0
  179. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/topics/topicns.xml +0 -0
  180. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -0
  181. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -0
  182. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -0
  183. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/analytics/humanface.xsd +0 -0
  184. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/analytics/index.html +0 -0
  185. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -0
  186. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/analytics/rules.xsd +0 -0
  187. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -0
  188. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -0
  189. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -0
  190. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/media/wsdl/index.html +0 -0
  191. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -0
  192. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -0
  193. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -0
  194. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -0
  195. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -0
  196. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif/wsdl/ver20/util/operationIndex.html +0 -0
  197. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif_python.egg-info/dependency_links.txt +0 -0
  198. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif_python.egg-info/entry_points.txt +0 -0
  199. {onvif_python-0.2.11 → onvif_python-0.3.1}/onvif_python.egg-info/top_level.txt +0 -0
  200. {onvif_python-0.2.11 → onvif_python-0.3.1}/setup.cfg +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,103 @@
1
+ Metadata-Version: 2.4
2
+ Name: onvif-python
3
+ Version: 0.3.1
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://deepwiki.com/nirsimetri/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.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Intended Audience :: Developers
22
+ Classifier: Intended Audience :: Information Technology
23
+ Classifier: Intended Audience :: Science/Research
24
+ Classifier: Topic :: Software Development :: Libraries
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Topic :: Security
27
+ Classifier: Topic :: Multimedia :: Video
28
+ Classifier: Topic :: Multimedia :: Sound/Audio
29
+ Classifier: Topic :: Communications
30
+ Classifier: Topic :: Text Processing :: Markup :: XML
31
+ Classifier: Natural Language :: English
32
+ Classifier: Natural Language :: Indonesian
33
+ Requires-Python: >=3.9
34
+ Description-Content-Type: text/markdown
35
+ License-File: LICENSE.md
36
+ Requires-Dist: zeep>=4.3.0
37
+ Requires-Dist: requests>=2.32.0
38
+ Requires-Dist: pyreadline3>=3.5.4; sys_platform == "win32"
39
+ Provides-Extra: dev
40
+ Requires-Dist: pytest>=7.0; 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
+ Dynamic: license-file
47
+
48
+ <h1 align="center">ONVIF Python</h1>
49
+
50
+ <div align="center">
51
+ <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>
52
+ <a href="https://pypi.org/project/onvif-python/"><img alt="PyPI Version" src="https://img.shields.io/badge/Version-0.3.1-orange?logo=python&logoColor=white&color=yellow&style=plastic"></a>
53
+ <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>
54
+ <br>
55
+ <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>
56
+ <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>
57
+ </div>
58
+
59
+ <h1 align="center">
60
+ <img src="https://raw.githubusercontent.com/nirsimetri/onvif-python/refs/heads/main/assets/images/carbon_onvif.png" alt="onvif" width="700px">
61
+ <br>
62
+ </h1>
63
+
64
+ **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.
65
+
66
+ **[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.
67
+
68
+ 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.
69
+
70
+ 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.
71
+
72
+ ## Who Is It For?
73
+ - **Individual developers** exploring ONVIF or building hobby projects
74
+ - **Companies** building video intelligence, analytics, or VMS platforms
75
+ - **Security integrators** who need reliable ONVIF interoperability across devices
76
+
77
+ ## Requirements
78
+
79
+ - **Python**: 3.9 or higher
80
+ - **Dependencies**:
81
+ - [`zeep>=4.3.0`](https://github.com/mvantellingen/python-zeep) - SOAP client for ONVIF communication
82
+ - [`requests>=2.32.0`](https://github.com/psf/requests) - HTTP library for network requests
83
+
84
+ ## Installation
85
+
86
+ From official [PyPI](https://pypi.org/project/onvif-python/):
87
+ ```bash
88
+ pip install --upgrade onvif-python
89
+ ```
90
+ Or clone this repository and install locally:
91
+ ```bash
92
+ git clone https://github.com/nirsimetri/onvif-python
93
+ cd onvif-python
94
+ pip install .
95
+ ```
96
+
97
+ ## Documentation
98
+
99
+ For complete guide, including usage examples and API references, please visit the [Documentation Page](https://nirsimetri.github.io/onvif-python/).
100
+
101
+ ## License
102
+
103
+ This project is licensed under the MIT License. See [LICENSE](./LICENSE.md) for details.
@@ -0,0 +1,56 @@
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.3.1-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.9 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
+ ```bash
41
+ pip install --upgrade onvif-python
42
+ ```
43
+ Or clone this repository and install locally:
44
+ ```bash
45
+ git clone https://github.com/nirsimetri/onvif-python
46
+ cd onvif-python
47
+ pip install .
48
+ ```
49
+
50
+ ## Documentation
51
+
52
+ For complete guide, including usage examples and API references, please visit the [Documentation Page](https://nirsimetri.github.io/onvif-python/).
53
+
54
+ ## License
55
+
56
+ This project is licensed under the MIT License. See [LICENSE](./LICENSE.md) for details.
@@ -0,0 +1,25 @@
1
+ """ONVIF Python."""
2
+
3
+ from onvif.cli import main as ONVIFCLI
4
+ from onvif.client import ONVIFClient
5
+ from onvif.operator import CacheMode
6
+ from onvif.utils import (
7
+ ONVIFWSDL,
8
+ ONVIFDiscovery,
9
+ ONVIFErrorHandler,
10
+ ONVIFOperationException,
11
+ ONVIFParser,
12
+ ZeepPatcher,
13
+ )
14
+
15
+ __all__ = [
16
+ "ONVIFClient",
17
+ "CacheMode",
18
+ "ONVIFWSDL",
19
+ "ONVIFOperationException",
20
+ "ONVIFErrorHandler",
21
+ "ZeepPatcher",
22
+ "ONVIFCLI",
23
+ "ONVIFDiscovery",
24
+ "ONVIFParser",
25
+ ]
@@ -0,0 +1,5 @@
1
+ """ONVIF CLI."""
2
+
3
+ from onvif.cli.main import main
4
+
5
+ __all__ = ["main"]
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env python3
2
+ """ONVIF CLI entry point for python -m onvif.cli."""
3
+
4
+ from onvif.cli.main import main
5
+
6
+ if __name__ == "__main__":
7
+ main()
@@ -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,124 @@
1
+ """ONVIF discovery helpers."""
2
+
3
+ from onvif.cli.utils import colorize
4
+ from onvif.utils import ONVIFDiscovery
5
+
6
+
7
+ def discover_devices(
8
+ timeout: int = 4,
9
+ interface: str | None = None,
10
+ prefer_https: bool = False,
11
+ filter_term: str | None = None,
12
+ ) -> list:
13
+ """Discover ONVIF devices on the network using WS-Discovery.
14
+
15
+ Args:
16
+ timeout (int): Discovery timeout in seconds
17
+ interface (str | None): Network interface to use for discovery
18
+ prefer_https (bool): If True, prioritize HTTPS XAddrs when available
19
+ filter_term (str | None): Optional search term to filter devices by types or scopes
20
+
21
+ Returns:
22
+ List of discovered devices with connection info
23
+ """
24
+ # Use ONVIFDiscovery class
25
+ discovery = ONVIFDiscovery(timeout=timeout, interface=interface)
26
+
27
+ print(f"\n{colorize('Discovering ONVIF devices on network...', 'yellow')}")
28
+ print(f"Network interface: {colorize(discovery.get_local_ip(), 'white')}")
29
+ print(f"Timeout: {timeout}s")
30
+ if filter_term:
31
+ print(f"Filter: {colorize(filter_term, 'yellow')}")
32
+ print()
33
+
34
+ devices = discovery.discover(prefer_https=prefer_https, search=filter_term)
35
+
36
+ return devices
37
+
38
+
39
+ def select_device_interactive(devices: list) -> tuple[str, int, bool] | None:
40
+ """Display devices and allow user to select one interactively.
41
+
42
+ Args:
43
+ devices (list): Discovered devices
44
+
45
+ Returns:
46
+ Tuple of (host, port, use_https) or None if cancelled
47
+ """
48
+ if not devices:
49
+ print(f"\n{colorize('No ONVIF devices found.', 'red')}")
50
+ return None
51
+
52
+ print(f"{colorize(f'Found {len(devices)} ONVIF device(s):', 'green')}")
53
+
54
+ for idx, device in enumerate(devices, 1):
55
+ idx_str = colorize(f"[{idx}]", "yellow")
56
+ protocol = "https" if device.get("use_https", False) else "http"
57
+ host_port = f"{device['host']}:{device['port']}"
58
+ protocol_indicator = (
59
+ colorize("🔒 HTTPS", "green")
60
+ if device.get("use_https", False)
61
+ else colorize("HTTP", "white")
62
+ )
63
+ print(f"\n{idx_str} {colorize(host_port, 'yellow')} ({protocol_indicator})")
64
+
65
+ # Remove uuid: or urn:uuid: prefix from EPR
66
+ epr_display = device["epr"]
67
+ if epr_display.startswith("urn:uuid:"):
68
+ epr_display = epr_display.replace("urn:uuid:", "")
69
+ elif epr_display.startswith("uuid:"):
70
+ epr_display = epr_display.replace("uuid:", "")
71
+ print(f" [uuid] {epr_display}")
72
+
73
+ if device["xaddrs"]:
74
+ xaddrs_parts = [f"[{xaddr}]" for xaddr in device["xaddrs"]]
75
+ print(f" [xaddrs] {' '.join(xaddrs_parts)}")
76
+
77
+ if device["types"]:
78
+ types_parts = [f"[{t}]" for t in device["types"]]
79
+ print(f" [types] {' '.join(types_parts)}")
80
+
81
+ if device["scopes"]:
82
+ scope_parts = []
83
+ for scope in device["scopes"]:
84
+ # Remove the prefix "onvif://www.onvif.org/" if present
85
+ if scope.startswith("onvif://www.onvif.org/"):
86
+ simplified = scope.replace("onvif://www.onvif.org/", "")
87
+ scope_parts.append(f"[{simplified}]")
88
+ else:
89
+ # Keep other scopes as-is (e.g., http:123)
90
+ scope_parts.append(f"[{scope}]")
91
+
92
+ if scope_parts:
93
+ print(f" [scopes] {' '.join(scope_parts)}")
94
+
95
+ # Simple selection (without arrow keys for cross-platform compatibility)
96
+ while True:
97
+ try:
98
+ selection = input(
99
+ f"\nSelect device number {colorize(f'1-{len(devices)}', 'white')} or {colorize('q', 'white')} to quit: "
100
+ )
101
+
102
+ if selection.lower() == "q":
103
+ return None
104
+
105
+ idx = int(selection)
106
+ if 1 <= idx <= len(devices):
107
+ selected = devices[idx - 1]
108
+ protocol = "https" if selected.get("use_https", False) else "http"
109
+ host_port = f"{selected['host']}:{selected['port']}"
110
+ print(
111
+ f"\n{colorize('Selected:', 'green')} {colorize(protocol, 'cyan')}://{colorize(host_port, 'yellow')}"
112
+ )
113
+ return (
114
+ selected["host"],
115
+ selected["port"],
116
+ selected.get("use_https", False),
117
+ )
118
+ else:
119
+ print(colorize("Invalid selection. Please try again.", "red"))
120
+
121
+ except ValueError:
122
+ print(colorize("Invalid input. Please enter a number.", "red"))
123
+ except (EOFError, KeyboardInterrupt):
124
+ 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,189 @@
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
+ def _serialize_for_json(obj: Any) -> Any:
106
+ """Recursively serialize ONVIF objects for JSON output.
107
+
108
+ Args:
109
+ obj: Object to serialize
110
+
111
+ Returns:
112
+ JSON-serializable representation of the object
113
+ """
114
+ if obj is None:
115
+ return None
116
+ elif isinstance(obj, (str, int, float, bool)):
117
+ return obj
118
+ elif isinstance(obj, datetime):
119
+ return obj.isoformat()
120
+ elif isinstance(obj, (list, tuple)):
121
+ return [_serialize_for_json(item) for item in obj]
122
+ elif isinstance(obj, dict):
123
+ return {key: _serialize_for_json(value) for key, value in obj.items()}
124
+
125
+ # Check if this is a Zeep object (has _xsd_type attribute)
126
+ elif hasattr(obj, "_xsd_type"):
127
+ result = {}
128
+ # Try to get all elements from XSD type
129
+ if hasattr(obj._xsd_type, "elements"): # pylint: disable=protected-access
130
+ for (
131
+ elem_name,
132
+ _,
133
+ ) in obj._xsd_type.elements: # pylint: disable=protected-access
134
+ try:
135
+ value = getattr(obj, elem_name, None)
136
+ if value is not None:
137
+ result[elem_name] = _serialize_for_json(value)
138
+ except (AttributeError, TypeError):
139
+ # Skip elements that can't be accessed or have type issues
140
+ pass
141
+
142
+ # Also try regular attributes
143
+ for attr_name in dir(obj):
144
+ if not attr_name.startswith("_") and not callable(
145
+ getattr(obj, attr_name, None)
146
+ ):
147
+ try:
148
+ attr_value = getattr(obj, attr_name)
149
+ if attr_value is not None and attr_name not in result:
150
+ result[attr_name] = _serialize_for_json(attr_value)
151
+ except (AttributeError, TypeError):
152
+ # Skip attributes that can't be accessed or have type issues
153
+ pass
154
+
155
+ return result
156
+
157
+ elif hasattr(obj, "__dict__"):
158
+ # Handle regular objects with attributes
159
+ result = {}
160
+ for key, value in obj.__dict__.items():
161
+ if not key.startswith("_"): # Skip private attributes
162
+ result[key] = _serialize_for_json(value)
163
+
164
+ # If result is empty, try to get attributes using dir()
165
+ if not result:
166
+ for attr_name in dir(obj):
167
+ if not attr_name.startswith("_") and not callable(
168
+ getattr(obj, attr_name, None)
169
+ ):
170
+ try:
171
+ attr_value = getattr(obj, attr_name)
172
+ if attr_value is not None:
173
+ result[attr_name] = _serialize_for_json(attr_value)
174
+ except (AttributeError, TypeError):
175
+ # Skip attributes that can't be accessed or have type issues
176
+ pass
177
+
178
+ return result
179
+ elif hasattr(obj, "_value_1"):
180
+ # Handle zeep objects with special structure
181
+ return _serialize_for_json(obj._value_1) # pylint: disable=protected-access
182
+ else:
183
+ # Try to convert to dict using vars() if available
184
+ try:
185
+ obj_dict = vars(obj)
186
+ return _serialize_for_json(obj_dict)
187
+ except TypeError:
188
+ # Fallback to string representation
189
+ return str(obj)