onvif-python 0.4.2__tar.gz → 0.4.3__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.
- onvif_python-0.4.2/LICENSE.md → onvif_python-0.4.3/LICENSE +1 -1
- onvif_python-0.4.3/NOTICE +11 -0
- {onvif_python-0.4.2/onvif_python.egg-info → onvif_python-0.4.3}/PKG-INFO +34 -34
- {onvif_python-0.4.2 → onvif_python-0.4.3}/README.md +10 -15
- onvif_python-0.4.3/onvif/cli/__init__.py +1 -0
- onvif_python-0.4.3/onvif/cli/interactive/__init__.py +5 -0
- onvif_python-0.4.3/onvif/cli/interactive/context.py +55 -0
- onvif_python-0.4.3/onvif/cli/interactive/information.py +175 -0
- onvif_python-0.4.3/onvif/cli/interactive/navigation.py +185 -0
- onvif_python-0.4.3/onvif/cli/interactive/reference.py +204 -0
- onvif_python-0.4.3/onvif/cli/interactive/service.py +325 -0
- onvif_python-0.4.3/onvif/cli/interactive/shell.py +246 -0
- onvif_python-0.4.3/onvif/cli/interactive/utils.py +313 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/cli/main.py +1 -1
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/client.py +2 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/db/products.db +1 -1
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/meta.py +1 -1
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/operator.py +92 -2
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/deviceio.py +3 -3
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/devicemgmt.py +3 -3
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/media2.py +25 -6
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/ptz.py +1 -1
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/recording.py +5 -4
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/security/tlsserver.py +9 -12
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/utils/error_handlers.py +2 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/utils/exceptions.py +3 -1
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/utils/service.py +39 -1
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/utils/zeep.py +2 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3/onvif_python.egg-info}/PKG-INFO +34 -34
- onvif_python-0.4.3/onvif_python.egg-info/SOURCES.txt +90 -0
- onvif_python-0.4.3/onvif_python.egg-info/requires.txt +24 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/pyproject.toml +43 -24
- {onvif_python-0.4.2 → onvif_python-0.4.3}/tests/test_client.py +156 -2
- onvif_python-0.4.3/tests/test_client_services.py +121 -0
- onvif_python-0.4.3/tests/test_core.py +140 -0
- onvif_python-0.4.3/tests/test_meta.py +26 -0
- onvif_python-0.4.3/tests/test_operator.py +946 -0
- onvif_python-0.4.2/MANIFEST.in +0 -3
- onvif_python-0.4.2/onvif/cli/__init__.py +0 -5
- onvif_python-0.4.2/onvif/cli/interactive.py +0 -1335
- onvif_python-0.4.2/onvif/wsdl/schema/common/b-2.xsd +0 -580
- onvif_python-0.4.2/onvif/wsdl/schema/common/bf-2.xsd +0 -84
- onvif_python-0.4.2/onvif/wsdl/schema/common/bw-2.wsdl +0 -448
- onvif_python-0.4.2/onvif/wsdl/schema/common/include +0 -13
- onvif_python-0.4.2/onvif/wsdl/schema/common/mpqf.xsd +0 -1109
- onvif_python-0.4.2/onvif/wsdl/schema/common/r-2.xsd +0 -50
- onvif_python-0.4.2/onvif/wsdl/schema/common/rw-2.wsdl +0 -54
- onvif_python-0.4.2/onvif/wsdl/schema/common/soap-envelope +0 -165
- onvif_python-0.4.2/onvif/wsdl/schema/common/t-1.xsd +0 -185
- onvif_python-0.4.2/onvif/wsdl/schema/common/ws-addr.xsd +0 -144
- onvif_python-0.4.2/onvif/wsdl/schema/common/xml.xsd +0 -287
- onvif_python-0.4.2/onvif/wsdl/schema/common/xmlmime +0 -49
- onvif_python-0.4.2/onvif/wsdl/ver10/accesscontrol/wsdl/index.html +0 -7
- onvif_python-0.4.2/onvif/wsdl/ver10/accessrules/wsdl/accessrules.wsdl +0 -702
- onvif_python-0.4.2/onvif/wsdl/ver10/accessrules/wsdl/index.html +0 -7
- onvif_python-0.4.2/onvif/wsdl/ver10/actionengine.wsdl +0 -1265
- onvif_python-0.4.2/onvif/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl +0 -4205
- onvif_python-0.4.2/onvif/wsdl/ver10/advancedsecurity/wsdl/index.html +0 -7
- onvif_python-0.4.2/onvif/wsdl/ver10/analyticsdevice.wsdl +0 -709
- onvif_python-0.4.2/onvif/wsdl/ver10/appmgmt/wsdl/appmgmt.wsdl +0 -502
- onvif_python-0.4.2/onvif/wsdl/ver10/appmgmt/wsdl/index.html +0 -7
- onvif_python-0.4.2/onvif/wsdl/ver10/authenticationbehavior/wsdl/authenticationbehavior.wsdl +0 -1382
- onvif_python-0.4.2/onvif/wsdl/ver10/authenticationbehavior/wsdl/index.html +0 -7
- onvif_python-0.4.2/onvif/wsdl/ver10/credential/wsdl/credential.wsdl +0 -2018
- onvif_python-0.4.2/onvif/wsdl/ver10/credential/wsdl/index.html +0 -7
- onvif_python-0.4.2/onvif/wsdl/ver10/device/wsdl/devicemgmt.wsdl +0 -4786
- onvif_python-0.4.2/onvif/wsdl/ver10/deviceio.wsdl +0 -1414
- onvif_python-0.4.2/onvif/wsdl/ver10/display/display.wsdl +0 -383
- onvif_python-0.4.2/onvif/wsdl/ver10/display/wsdl/index.htm +0 -383
- onvif_python-0.4.2/onvif/wsdl/ver10/display.wsdl +0 -521
- onvif_python-0.4.2/onvif/wsdl/ver10/doorcontrol/wsdl/index.html +0 -7
- onvif_python-0.4.2/onvif/wsdl/ver10/events/wsdl/bw-2-vs-mod.wsdl +0 -449
- onvif_python-0.4.2/onvif/wsdl/ver10/events/wsdl/event-vs.wsdl +0 -947
- onvif_python-0.4.2/onvif/wsdl/ver10/events/wsdl/event.wsdl +0 -947
- onvif_python-0.4.2/onvif/wsdl/ver10/events/wsdl/index.html +0 -7
- onvif_python-0.4.2/onvif/wsdl/ver10/federatedsearch.wsdl +0 -344
- onvif_python-0.4.2/onvif/wsdl/ver10/media/wsdl/media.wsdl +0 -3684
- onvif_python-0.4.2/onvif/wsdl/ver10/pacs/accesscontrol.wsdl +0 -1942
- onvif_python-0.4.2/onvif/wsdl/ver10/pacs/doorcontrol.wsdl +0 -1792
- onvif_python-0.4.2/onvif/wsdl/ver10/pacs/types.xsd +0 -181
- onvif_python-0.4.2/onvif/wsdl/ver10/provisioning/wsdl/provisioning.wsdl +0 -588
- onvif_python-0.4.2/onvif/wsdl/ver10/receiver.wsdl +0 -398
- onvif_python-0.4.2/onvif/wsdl/ver10/recording.wsdl +0 -1442
- onvif_python-0.4.2/onvif/wsdl/ver10/replay.wsdl +0 -221
- onvif_python-0.4.2/onvif/wsdl/ver10/schedule/wsdl/index.html +0 -7
- onvif_python-0.4.2/onvif/wsdl/ver10/schedule/wsdl/schedule.wsdl +0 -1383
- onvif_python-0.4.2/onvif/wsdl/ver10/schema/common.xsd +0 -431
- onvif_python-0.4.2/onvif/wsdl/ver10/schema/metadatastream.xsd +0 -534
- onvif_python-0.4.2/onvif/wsdl/ver10/schema/onvif.xsd +0 -9743
- onvif_python-0.4.2/onvif/wsdl/ver10/search.wsdl +0 -1114
- onvif_python-0.4.2/onvif/wsdl/ver10/thermal/wsdl/index.html +0 -7
- onvif_python-0.4.2/onvif/wsdl/ver10/thermal/wsdl/thermal.wsdl +0 -777
- onvif_python-0.4.2/onvif/wsdl/ver10/topics/topicns.xml +0 -13
- onvif_python-0.4.2/onvif/wsdl/ver10/uplink/wsdl/index.html +0 -7
- onvif_python-0.4.2/onvif/wsdl/ver10/uplink/wsdl/uplink.wsdl +0 -256
- onvif_python-0.4.2/onvif/wsdl/ver20/analytics/humanbody.xsd +0 -490
- onvif_python-0.4.2/onvif/wsdl/ver20/analytics/humanface.xsd +0 -501
- onvif_python-0.4.2/onvif/wsdl/ver20/analytics/index.html +0 -7
- onvif_python-0.4.2/onvif/wsdl/ver20/analytics/radiometry.xsd +0 -389
- onvif_python-0.4.2/onvif/wsdl/ver20/analytics/rules.xsd +0 -127
- onvif_python-0.4.2/onvif/wsdl/ver20/analytics/wsdl/analytics.wsdl +0 -757
- onvif_python-0.4.2/onvif/wsdl/ver20/analytics/wsdl/index.html +0 -7
- onvif_python-0.4.2/onvif/wsdl/ver20/imaging/wsdl/imaging.wsdl +0 -603
- onvif_python-0.4.2/onvif/wsdl/ver20/media/wsdl/index.html +0 -7
- onvif_python-0.4.2/onvif/wsdl/ver20/media/wsdl/media.wsdl +0 -3126
- onvif_python-0.4.2/onvif/wsdl/ver20/ptz/wsdl/ptz.wsdl +0 -1450
- onvif_python-0.4.2/onvif/wsdl/ver20/util/federated_search_documentation.html +0 -1647
- onvif_python-0.4.2/onvif/wsdl/ver20/util/fedsearch-docu-viewer.xsl +0 -8
- onvif_python-0.4.2/onvif/wsdl/ver20/util/onvif-wsdl-viewer.xsl +0 -2580
- onvif_python-0.4.2/onvif/wsdl/ver20/util/operationIndex.html +0 -97
- onvif_python-0.4.2/onvif_python.egg-info/SOURCES.txt +0 -152
- onvif_python-0.4.2/onvif_python.egg-info/requires.txt +0 -22
- onvif_python-0.4.2/tests/test_core.py +0 -386
- onvif_python-0.4.2/tests/test_error_handlers.py +0 -362
- onvif_python-0.4.2/tests/test_exceptions.py +0 -340
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/__init__.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/cli/__main__.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/cli/helpers/__init__.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/cli/helpers/discovery.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/cli/helpers/execution.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/cli/helpers/files.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/cli/helpers/messages.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/cli/helpers/products.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/cli/helpers/wsdl_parser.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/cli/utils.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/db/companies.db +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/__init__.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/accesscontrol.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/accessrules.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/actionengine.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/analytics/__init__.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/analytics/analytics.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/analytics/ruleengine.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/analyticsdevice.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/appmgmt.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/authenticationbehavior.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/credential.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/display.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/doorcontrol.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/events/__init__.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/events/events.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/events/notification.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/events/pausable_subscription.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/events/pullpoint.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/events/subscription.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/imaging.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/media.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/provisioning.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/receiver.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/replay.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/schedule.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/search.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/security/__init__.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/security/advancedsecurity.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/security/authorizationserver.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/security/dot1x.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/security/jwt.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/security/keystore.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/security/mediasigning.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/thermal.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/services/uplink.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/utils/__init__.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/utils/discovery.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/utils/plugins.py +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif/utils/wsdl.py +0 -0
- /onvif_python-0.4.2/onvif/wsdl/LICENSE.md → /onvif_python-0.4.3/onvif/wsdl/LICENSE +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif_python.egg-info/dependency_links.txt +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif_python.egg-info/entry_points.txt +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/onvif_python.egg-info/top_level.txt +0 -0
- {onvif_python-0.4.2 → onvif_python-0.4.3}/setup.cfg +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
This project is an independent open-source implementation of the ONVIF (https://www.onvif.org) specifications. It is not affiliated with, endorsed by, or sponsored by ONVIF or its member companies.
|
|
2
|
+
|
|
3
|
+
- The name “ONVIF” and the ONVIF logo are registered trademarks of the ONVIF organization.
|
|
4
|
+
- Any references to ONVIF within this project are made strictly for the purpose of describing interoperability with ONVIF-compliant devices and services.
|
|
5
|
+
- Use of the ONVIF trademark in this repository is solely nominative and does not imply any partnership, certification, or official status.
|
|
6
|
+
- This project includes WSDL/XSD/HTML files from the official ONVIF specifications.
|
|
7
|
+
- These files are © ONVIF and are redistributed here for interoperability purposes.
|
|
8
|
+
- The WSDL files in the https://github.com/nirsimetri/onvif-python/tree/main/onvif/wsdl folder are distributed under the ONVIF Contributor License Agreement and Apache License 2.0. See https://github.com/nirsimetri/onvif-python/tree/main/onvif/wsdl/LICENSE for details.
|
|
9
|
+
- All rights to the ONVIF specifications are reserved by ONVIF.
|
|
10
|
+
|
|
11
|
+
If you require certified ONVIF-compliant devices or clients, please refer to the official https://www.onvif.org/conformant-products. For authoritative reference and the latest official ONVIF specifications, please consult the https://www.onvif.org/profiles/specifications/specification-history.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: onvif-python
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: A modern Python library for ONVIF-compliant devices
|
|
5
5
|
Author-email: Nirsimetri Technologies® <open@nirsimetri.com>
|
|
6
6
|
Maintainer: Kaburagi
|
|
@@ -12,15 +12,17 @@ Project-URL: Issues, https://github.com/nirsimetri/onvif-python/issues
|
|
|
12
12
|
Keywords: onvif,Camera,onvif python,ip camera,cctv,soap,surveillance,cctv cameras,soap client,onvif client,onvif library,onvif specifications
|
|
13
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
14
14
|
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Information Technology
|
|
17
|
+
Classifier: Intended Audience :: Science/Research
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
24
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
25
|
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
26
|
Classifier: Topic :: Software Development :: Libraries
|
|
25
27
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
28
|
Classifier: Topic :: Security
|
|
@@ -30,44 +32,42 @@ Classifier: Topic :: Communications
|
|
|
30
32
|
Classifier: Topic :: Text Processing :: Markup :: XML
|
|
31
33
|
Requires-Python: >=3.10
|
|
32
34
|
Description-Content-Type: text/markdown
|
|
33
|
-
License-File: LICENSE
|
|
34
|
-
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist:
|
|
35
|
+
License-File: LICENSE
|
|
36
|
+
License-File: NOTICE
|
|
37
|
+
Requires-Dist: zeep<4.4.0,>=4.3.0
|
|
38
|
+
Requires-Dist: requests<3.0.0,>=2.32.0
|
|
39
|
+
Requires-Dist: pyreadline3<4.0.0,>=3.5.4; sys_platform == "win32"
|
|
37
40
|
Provides-Extra: dev
|
|
38
|
-
Requires-Dist: pytest
|
|
39
|
-
Requires-Dist: pytest-cov; extra == "dev"
|
|
40
|
-
Requires-Dist: black; extra == "dev"
|
|
41
|
-
Requires-Dist: mypy; extra == "dev"
|
|
42
|
-
Requires-Dist: isort; extra == "dev"
|
|
43
|
-
Requires-Dist: pylint; extra == "dev"
|
|
44
|
-
Requires-Dist:
|
|
41
|
+
Requires-Dist: pytest<10.0,>=8.0; extra == "dev"
|
|
42
|
+
Requires-Dist: pytest-cov<7.0,>=6.0; extra == "dev"
|
|
43
|
+
Requires-Dist: black<27.0,>=26.0; extra == "dev"
|
|
44
|
+
Requires-Dist: mypy<2.0,>=1.0; extra == "dev"
|
|
45
|
+
Requires-Dist: isort<7.0,>=6.0; extra == "dev"
|
|
46
|
+
Requires-Dist: pylint<4.3.0,>=4.1.2; extra == "dev"
|
|
47
|
+
Requires-Dist: pylint-pytest<2.0,>=1.1; extra == "dev"
|
|
48
|
+
Requires-Dist: docformatter<2.0,>=1.0; extra == "dev"
|
|
49
|
+
Requires-Dist: codespell<3.0,>=2.0; extra == "dev"
|
|
45
50
|
Requires-Dist: lxml-stubs; extra == "dev"
|
|
46
51
|
Requires-Dist: types-setuptools; extra == "dev"
|
|
47
52
|
Requires-Dist: types-requests; extra == "dev"
|
|
48
53
|
Provides-Extra: docs
|
|
49
|
-
Requires-Dist: mkdocs-material; extra == "docs"
|
|
50
|
-
Requires-Dist: mkdocstrings[python]; extra == "docs"
|
|
51
|
-
Requires-Dist: mkdocs-git-authors-plugin; extra == "docs"
|
|
54
|
+
Requires-Dist: mkdocs-material<10.0.0,>=9.7.7; extra == "docs"
|
|
55
|
+
Requires-Dist: mkdocstrings[python]<3.0.0,>=1.0.0; extra == "docs"
|
|
56
|
+
Requires-Dist: mkdocs-git-authors-plugin<1.0.0,>=0.10.0; extra == "docs"
|
|
52
57
|
Dynamic: license-file
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
# ONVIF Python
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
[](https://pypi.org/project/onvif-python)
|
|
62
|
+
[](https://pypi.org/project/onvif-python/)
|
|
63
|
+
[](https://app.codacy.com/gh/nirsimetri/onvif-python/dashboard)
|
|
64
|
+
[](https://app.codacy.com/gh/nirsimetri/onvif-python/coverage)
|
|
65
|
+
[](https://pepy.tech/projects/onvif-python)
|
|
60
66
|
<br>
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
<h1 align="center">
|
|
66
|
-
<img src="https://raw.githubusercontent.com/nirsimetri/onvif-python/refs/heads/main/assets/images/carbon_onvif.png" alt="onvif" width="700px">
|
|
67
|
-
<br>
|
|
68
|
-
</h1>
|
|
67
|
+
[](https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml)
|
|
68
|
+
[](https://github.com/nirsimetri/onvif-python/actions/workflows/python-publish.yml)
|
|
69
|
+
[](https://github.com/nirsimetri/onvif-python/actions/workflows/cli-cross-platform.yml)
|
|
69
70
|
|
|
70
|
-
**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.
|
|
71
71
|
|
|
72
72
|
**[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.
|
|
73
73
|
|
|
@@ -112,4 +112,4 @@ For complete guide, including usage examples and API references, please visit th
|
|
|
112
112
|
|
|
113
113
|
## License
|
|
114
114
|
|
|
115
|
-
This project is licensed under the MIT License. See [LICENSE](./LICENSE
|
|
115
|
+
This project is licensed under the MIT License. See [LICENSE](./LICENSE) for details.
|
|
@@ -1,20 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
# ONVIF Python
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
[](https://pypi.org/project/onvif-python)
|
|
4
|
+
[](https://pypi.org/project/onvif-python/)
|
|
5
|
+
[](https://app.codacy.com/gh/nirsimetri/onvif-python/dashboard)
|
|
6
|
+
[](https://app.codacy.com/gh/nirsimetri/onvif-python/coverage)
|
|
7
|
+
[](https://pepy.tech/projects/onvif-python)
|
|
7
8
|
<br>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
[](https://github.com/nirsimetri/onvif-python/actions/workflows/python-app.yml)
|
|
10
|
+
[](https://github.com/nirsimetri/onvif-python/actions/workflows/python-publish.yml)
|
|
11
|
+
[](https://github.com/nirsimetri/onvif-python/actions/workflows/cli-cross-platform.yml)
|
|
11
12
|
|
|
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
13
|
|
|
19
14
|
**[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
15
|
|
|
@@ -59,4 +54,4 @@ For complete guide, including usage examples and API references, please visit th
|
|
|
59
54
|
|
|
60
55
|
## License
|
|
61
56
|
|
|
62
|
-
This project is licensed under the MIT License. See [LICENSE](./LICENSE
|
|
57
|
+
This project is licensed under the MIT License. See [LICENSE](./LICENSE) for details.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""ONVIF CLI."""
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""ONVIF Interactive Shell context."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from onvif.client import ONVIFClient
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class ShellContext: # pylint: disable=too-many-instance-attributes
|
|
12
|
+
"""Store shared state for the interactive shell."""
|
|
13
|
+
|
|
14
|
+
UNSET = object()
|
|
15
|
+
|
|
16
|
+
client: ONVIFClient
|
|
17
|
+
args: object
|
|
18
|
+
|
|
19
|
+
current_service: object | None = None
|
|
20
|
+
current_service_name: str | None = None
|
|
21
|
+
stored_data: dict[str, Any] = field(
|
|
22
|
+
default_factory=dict
|
|
23
|
+
) # For storing command results
|
|
24
|
+
stored_metadata: dict[str, dict[str, Any]] = field(
|
|
25
|
+
default_factory=dict
|
|
26
|
+
) # For storing metadata about stored data (service, method
|
|
27
|
+
|
|
28
|
+
last_result: Any = field(default=UNSET, repr=False)
|
|
29
|
+
last_method: str | None = None
|
|
30
|
+
last_service_name: str | None = None
|
|
31
|
+
last_operation_timestamp: datetime | None = None
|
|
32
|
+
|
|
33
|
+
base_commands: list[str] = field(
|
|
34
|
+
default_factory=lambda: [
|
|
35
|
+
"capabilities",
|
|
36
|
+
"caps",
|
|
37
|
+
"services",
|
|
38
|
+
"help",
|
|
39
|
+
"exit",
|
|
40
|
+
"store",
|
|
41
|
+
"rm",
|
|
42
|
+
"show",
|
|
43
|
+
"cls",
|
|
44
|
+
"clear",
|
|
45
|
+
"info",
|
|
46
|
+
"debug",
|
|
47
|
+
"ls",
|
|
48
|
+
"cd",
|
|
49
|
+
"shortcuts",
|
|
50
|
+
"desc",
|
|
51
|
+
"type",
|
|
52
|
+
]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
device_info_text: str | None = None
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""Information commands implementation."""
|
|
2
|
+
|
|
3
|
+
import cmd
|
|
4
|
+
from typing import Callable
|
|
5
|
+
|
|
6
|
+
from requests.exceptions import RequestException
|
|
7
|
+
from zeep.exceptions import TransportError
|
|
8
|
+
|
|
9
|
+
from onvif.cli.helpers.messages import INTERACTIVE_HELP, INTERACTIVE_SHORTCUTS
|
|
10
|
+
from onvif.cli.utils import (
|
|
11
|
+
colorize,
|
|
12
|
+
format_capabilities_as_services,
|
|
13
|
+
format_services_list,
|
|
14
|
+
)
|
|
15
|
+
from onvif.utils.exceptions import ONVIFOperationException
|
|
16
|
+
|
|
17
|
+
from .context import ShellContext
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class InformationCommands:
|
|
21
|
+
"""Provide information commands."""
|
|
22
|
+
|
|
23
|
+
context: ShellContext
|
|
24
|
+
_handle_connection_error: Callable[..., None]
|
|
25
|
+
|
|
26
|
+
def do_help(self, arg):
|
|
27
|
+
"""Show help information."""
|
|
28
|
+
if arg:
|
|
29
|
+
cmd.Cmd.do_help(self, arg)
|
|
30
|
+
else:
|
|
31
|
+
print(INTERACTIVE_HELP)
|
|
32
|
+
|
|
33
|
+
def do_shortcuts(self, _line):
|
|
34
|
+
"""Show available shortcuts."""
|
|
35
|
+
print(INTERACTIVE_SHORTCUTS)
|
|
36
|
+
|
|
37
|
+
def do_info(self, _line):
|
|
38
|
+
"""Show connection and device information."""
|
|
39
|
+
# Build connection and CLI options info
|
|
40
|
+
options_info = []
|
|
41
|
+
|
|
42
|
+
# Connection options
|
|
43
|
+
if hasattr(self.context.args, "https") and self.context.args.https:
|
|
44
|
+
options_info.append(f" Use HTTPS : {colorize('True', 'green')}")
|
|
45
|
+
|
|
46
|
+
if (
|
|
47
|
+
hasattr(self.context.args, "no_verify_ssl")
|
|
48
|
+
and self.context.args.no_verify_ssl
|
|
49
|
+
):
|
|
50
|
+
options_info.append(f" Verify SSL : {colorize('False', 'red')}")
|
|
51
|
+
|
|
52
|
+
if (
|
|
53
|
+
hasattr(self.context.args, "timeout") and self.context.args.timeout != 10
|
|
54
|
+
): # 10 is default
|
|
55
|
+
options_info.append(
|
|
56
|
+
f" Timeout : {colorize(f'{self.context.args.timeout}s', 'yellow')}"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# CLI options
|
|
60
|
+
if hasattr(self.context.args, "debug") and self.context.args.debug:
|
|
61
|
+
options_info.append(f" Debug Mode : {colorize('True', 'green')}")
|
|
62
|
+
|
|
63
|
+
if hasattr(self.context.args, "no_patch") and self.context.args.no_patch:
|
|
64
|
+
options_info.append(f" ZeepPatcher : {colorize('Disabled', 'red')}")
|
|
65
|
+
|
|
66
|
+
if hasattr(self.context.args, "wsdl") and self.context.args.wsdl:
|
|
67
|
+
options_info.append(
|
|
68
|
+
f" Custom WSDL : {colorize(self.context.args.wsdl, 'yellow')}"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if (
|
|
72
|
+
hasattr(self.context.args, "health_check_interval")
|
|
73
|
+
and self.context.args.health_check_interval != 10
|
|
74
|
+
): # 10 is default
|
|
75
|
+
options_info.append(
|
|
76
|
+
f" Health Check : every {colorize(f'{self.context.args.health_check_interval}s', 'yellow')}"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Format options info
|
|
80
|
+
options_display = ""
|
|
81
|
+
if options_info:
|
|
82
|
+
options_display = "\n" + "\n".join(options_info)
|
|
83
|
+
|
|
84
|
+
# Display connection and device information using stored data
|
|
85
|
+
print(
|
|
86
|
+
f"\n{colorize('[ONVIF Terminal Client]', 'yellow')}"
|
|
87
|
+
f"\n Connected to : "
|
|
88
|
+
f"{colorize(f'{self.context.args.host}:{self.context.args.port}', 'yellow')}"
|
|
89
|
+
f"\n Auth Method : "
|
|
90
|
+
f"{colorize('HTTP Digest' if self.context.args.digest else 'WS-UsernameToken', 'yellow')}"
|
|
91
|
+
f"{options_display}{self.context.device_info_text}"
|
|
92
|
+
)
|
|
93
|
+
print() # Extra newline for spacing
|
|
94
|
+
|
|
95
|
+
def do_capabilities(self, _line):
|
|
96
|
+
"""Show device capabilities in service format."""
|
|
97
|
+
try:
|
|
98
|
+
if (
|
|
99
|
+
hasattr(self.context.client, "capabilities")
|
|
100
|
+
and self.context.client.capabilities
|
|
101
|
+
):
|
|
102
|
+
result = self.context.client.capabilities
|
|
103
|
+
else:
|
|
104
|
+
result = self.context.client.devicemgmt().GetCapabilities(
|
|
105
|
+
Category="All"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
self.context.stored_data["capabilities"] = result
|
|
109
|
+
self.context.stored_metadata["capabilities"] = {
|
|
110
|
+
"service": "devicemgmt",
|
|
111
|
+
"method": "GetCapabilities",
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# Use special formatting for capabilities
|
|
115
|
+
output = format_capabilities_as_services(result)
|
|
116
|
+
print(output)
|
|
117
|
+
except ONVIFOperationException as e:
|
|
118
|
+
if isinstance(e.original_exception, (RequestException, TransportError)):
|
|
119
|
+
self._handle_connection_error()
|
|
120
|
+
else:
|
|
121
|
+
print(f"{colorize('Error:', 'red')} {e}")
|
|
122
|
+
|
|
123
|
+
def do_caps(self, line):
|
|
124
|
+
"""Show device capabilities (alias for 'capabilities')"""
|
|
125
|
+
return self.do_capabilities(line)
|
|
126
|
+
|
|
127
|
+
def do_services(self, _line):
|
|
128
|
+
"""Show available ONVIF services in service format."""
|
|
129
|
+
try:
|
|
130
|
+
if (
|
|
131
|
+
hasattr(self.context.client, "services")
|
|
132
|
+
and self.context.client.services
|
|
133
|
+
):
|
|
134
|
+
result = self.context.client.services
|
|
135
|
+
else:
|
|
136
|
+
result = self.context.client.devicemgmt().GetServices(
|
|
137
|
+
IncludeCapability=False
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
self.context.stored_data["services"] = result
|
|
141
|
+
self.context.stored_metadata["services"] = {
|
|
142
|
+
"service": "devicemgmt",
|
|
143
|
+
"method": "GetServices",
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# Use special formatting for services
|
|
147
|
+
output = format_services_list(result)
|
|
148
|
+
print(output)
|
|
149
|
+
except ONVIFOperationException as e:
|
|
150
|
+
if isinstance(e.original_exception, (RequestException, TransportError)):
|
|
151
|
+
self._handle_connection_error()
|
|
152
|
+
else:
|
|
153
|
+
print(f"{colorize('Error:', 'red')} {e}")
|
|
154
|
+
|
|
155
|
+
def do_debug(self, _line):
|
|
156
|
+
"""Show debug information."""
|
|
157
|
+
if self.context.client.xml_plugin:
|
|
158
|
+
if self.context.last_method and self.context.last_operation_timestamp:
|
|
159
|
+
print(f"{colorize('Last Operation:', 'cyan')}")
|
|
160
|
+
print(
|
|
161
|
+
f" operation: {self.context.last_service_name}.{self.context.last_method}()"
|
|
162
|
+
)
|
|
163
|
+
print(
|
|
164
|
+
f" timestamp: {self.context.last_operation_timestamp.strftime('%Y-%m-%d %H:%M:%S')}\n"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
print(f"{colorize('Last SOAP Request:', 'cyan')}")
|
|
168
|
+
print(self.context.client.xml_plugin.last_sent_xml or "None")
|
|
169
|
+
print(f"\n{colorize('Last SOAP Response:', 'cyan')}")
|
|
170
|
+
print(self.context.client.xml_plugin.last_received_xml or "None")
|
|
171
|
+
else:
|
|
172
|
+
print(f"{colorize('Debug mode not enabled', 'yellow')}")
|
|
173
|
+
print(
|
|
174
|
+
f"Start CLI with {colorize('--debug', 'white')} flag to enable XML capture"
|
|
175
|
+
)
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Navigation commands implementation."""
|
|
2
|
+
|
|
3
|
+
import traceback
|
|
4
|
+
from typing import Callable
|
|
5
|
+
|
|
6
|
+
from onvif.cli.utils import (
|
|
7
|
+
colorize,
|
|
8
|
+
get_device_available_services,
|
|
9
|
+
get_service_methods,
|
|
10
|
+
get_service_required_args,
|
|
11
|
+
parse_json_params,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from .context import ShellContext
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class NavigationCommands:
|
|
18
|
+
"""Provide navigation commands."""
|
|
19
|
+
|
|
20
|
+
context: ShellContext
|
|
21
|
+
_display_grid: Callable[..., None]
|
|
22
|
+
update_prompt: Callable[[], None]
|
|
23
|
+
|
|
24
|
+
def do_ls(self, _line):
|
|
25
|
+
"""List available commands/services like TAB completion."""
|
|
26
|
+
if self.context.current_service:
|
|
27
|
+
# In service mode - show available methods
|
|
28
|
+
methods = get_service_methods(self.context.current_service)
|
|
29
|
+
# Add helper commands in service mode
|
|
30
|
+
methods.extend([colorize("type", "yellow"), colorize("desc", "yellow")])
|
|
31
|
+
if methods:
|
|
32
|
+
# Use the same display format as TAB completion
|
|
33
|
+
self._display_grid(methods)
|
|
34
|
+
else:
|
|
35
|
+
print("No methods available")
|
|
36
|
+
else:
|
|
37
|
+
# In root mode - show device-specific services and commands
|
|
38
|
+
services = get_device_available_services(self.context.client)
|
|
39
|
+
commands = self.context.base_commands
|
|
40
|
+
all_items = services + commands
|
|
41
|
+
|
|
42
|
+
if all_items:
|
|
43
|
+
# Use the same display format as TAB completion
|
|
44
|
+
self._display_grid(all_items)
|
|
45
|
+
else:
|
|
46
|
+
print("No commands available")
|
|
47
|
+
|
|
48
|
+
def do_cd(self, line):
|
|
49
|
+
"""Change to service directory (alias for entering service)"""
|
|
50
|
+
if not line:
|
|
51
|
+
print("Usage: cd <service_name>")
|
|
52
|
+
available_services = get_device_available_services(self.context.client)
|
|
53
|
+
colored_services = [colorize(svc, "cyan") for svc in available_services]
|
|
54
|
+
print(f"Available services: {', '.join(colored_services)}")
|
|
55
|
+
return None
|
|
56
|
+
return self.do_enter_service(line)
|
|
57
|
+
|
|
58
|
+
def complete_cd(self, text, _line, _begidx, _endidx):
|
|
59
|
+
"""Autocomplete service names for cd command."""
|
|
60
|
+
services = get_device_available_services(self.context.client)
|
|
61
|
+
return [s for s in services if s.lower().startswith(text.lower())]
|
|
62
|
+
|
|
63
|
+
def do_up(self, _line):
|
|
64
|
+
"""Exit current service mode (go up one level)"""
|
|
65
|
+
if self.context.current_service:
|
|
66
|
+
print(
|
|
67
|
+
f"{colorize('Exited service:', 'yellow')} "
|
|
68
|
+
f"{colorize(f'{self.context.current_service_name}', 'cyan')}"
|
|
69
|
+
)
|
|
70
|
+
self.context.current_service = None
|
|
71
|
+
self.context.current_service_name = None
|
|
72
|
+
self.update_prompt()
|
|
73
|
+
else:
|
|
74
|
+
print("Not in service mode")
|
|
75
|
+
|
|
76
|
+
# pylint: disable=too-many-locals,too-many-branches,too-many-statements
|
|
77
|
+
def do_enter_service(self, line):
|
|
78
|
+
"""Enter service mode with optional arguments for services that require them."""
|
|
79
|
+
# Parse service name and arguments
|
|
80
|
+
parts = line.split(None, 1)
|
|
81
|
+
service_name = parts[0] if parts else ""
|
|
82
|
+
args_str = parts[1] if len(parts) > 1 else ""
|
|
83
|
+
|
|
84
|
+
available_services = get_device_available_services(self.context.client)
|
|
85
|
+
if service_name not in available_services:
|
|
86
|
+
print(f"{colorize('Error:', 'red')} Unknown service '{service_name}'")
|
|
87
|
+
colored_services = [colorize(svc, "cyan") for svc in available_services]
|
|
88
|
+
print(f"Available services: {', '.join(colored_services)}")
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
# Check if this service requires arguments
|
|
93
|
+
required_args = get_service_required_args(service_name)
|
|
94
|
+
|
|
95
|
+
if required_args:
|
|
96
|
+
# Service requires arguments
|
|
97
|
+
if not args_str:
|
|
98
|
+
# No arguments provided, show help
|
|
99
|
+
print(
|
|
100
|
+
f"{colorize('Error:', 'red')} Service '{service_name}' requires arguments"
|
|
101
|
+
)
|
|
102
|
+
print(
|
|
103
|
+
f"{colorize('Usage:', 'yellow')} {service_name} "
|
|
104
|
+
f"{' '.join([f'{arg}=<value>' for arg in required_args])}"
|
|
105
|
+
)
|
|
106
|
+
print(
|
|
107
|
+
f"{colorize('Example:', 'yellow')} "
|
|
108
|
+
f"{service_name} {required_args[0]}=$subscription"
|
|
109
|
+
)
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
# Parse arguments
|
|
113
|
+
try:
|
|
114
|
+
parsed_args = parse_json_params(args_str)
|
|
115
|
+
except (OSError, AttributeError, ValueError, TypeError) as e:
|
|
116
|
+
print(f"{colorize('Error parsing arguments:', 'red')} {e}")
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
# Check if all required arguments are provided
|
|
120
|
+
missing_args = [arg for arg in required_args if arg not in parsed_args]
|
|
121
|
+
if missing_args:
|
|
122
|
+
print(
|
|
123
|
+
f"{colorize('Error:', 'red')} "
|
|
124
|
+
f"Missing required arguments: {', '.join(missing_args)}"
|
|
125
|
+
)
|
|
126
|
+
print(
|
|
127
|
+
f"{colorize('Usage:', 'yellow')} {service_name} "
|
|
128
|
+
f"{' '.join([f'{arg}=<value>' for arg in required_args])}"
|
|
129
|
+
)
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
# Resolve stored references in arguments
|
|
133
|
+
for arg_name in required_args:
|
|
134
|
+
arg_value = parsed_args[arg_name]
|
|
135
|
+
if isinstance(arg_value, str) and arg_value.startswith("$"):
|
|
136
|
+
# Resolve stored reference
|
|
137
|
+
reference = arg_value[1:] # Remove $ prefix
|
|
138
|
+
resolved_value = self._resolve_stored_reference(reference)
|
|
139
|
+
if resolved_value is None:
|
|
140
|
+
print(
|
|
141
|
+
f"{colorize('Error:', 'red')} "
|
|
142
|
+
f"Could not resolve reference '{arg_value}'"
|
|
143
|
+
)
|
|
144
|
+
return
|
|
145
|
+
parsed_args[arg_name] = resolved_value
|
|
146
|
+
|
|
147
|
+
# Call service method with arguments
|
|
148
|
+
service_method = getattr(self.context.client, service_name)
|
|
149
|
+
service = service_method(**parsed_args)
|
|
150
|
+
else:
|
|
151
|
+
# Service doesn't require arguments
|
|
152
|
+
service = getattr(self.context.client, service_name)()
|
|
153
|
+
|
|
154
|
+
self.context.current_service = service
|
|
155
|
+
self.context.current_service_name = service_name
|
|
156
|
+
self.update_prompt()
|
|
157
|
+
|
|
158
|
+
print(
|
|
159
|
+
f"{colorize('Entered service:', 'yellow')} {colorize(service_name, 'cyan')}"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Show available methods
|
|
163
|
+
methods = get_service_methods(service)
|
|
164
|
+
methods_preview = ", ".join(methods[:10])
|
|
165
|
+
if len(methods) > 10:
|
|
166
|
+
print(
|
|
167
|
+
f"{colorize('Available methods:', 'yellow')} {methods_preview} ... "
|
|
168
|
+
f"and {colorize(f'{len(methods) - 10} more.', 'yellow')}"
|
|
169
|
+
)
|
|
170
|
+
print(
|
|
171
|
+
f"Type {colorize('ls', 'cyan')}/press "
|
|
172
|
+
f"<{colorize('TAB', 'yellow')}> to see all."
|
|
173
|
+
)
|
|
174
|
+
else:
|
|
175
|
+
print(f"{colorize('Available methods:', 'yellow')} {methods_preview}")
|
|
176
|
+
print(f"Type {colorize('up', 'cyan')} to exit service mode.")
|
|
177
|
+
|
|
178
|
+
except (OSError, RuntimeError, ValueError, AttributeError) as e:
|
|
179
|
+
print(f"{colorize('Error entering service:', 'red')} {e}")
|
|
180
|
+
if self.context.args.debug:
|
|
181
|
+
traceback.print_exc()
|
|
182
|
+
|
|
183
|
+
def do_exit_service(self, line):
|
|
184
|
+
"""Exit current service mode (alias for 'up')"""
|
|
185
|
+
return self.do_up(line)
|