enapter 0.25.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.
- enapter-0.25.0/LICENSE +21 -0
- enapter-0.25.0/PKG-INFO +70 -0
- enapter-0.25.0/README.md +54 -0
- enapter-0.25.0/pyproject.toml +53 -0
- enapter-0.25.0/setup.cfg +4 -0
- enapter-0.25.0/src/enapter/__init__.py +13 -0
- enapter-0.25.0/src/enapter/__main__.py +9 -0
- enapter-0.25.0/src/enapter/async_/__init__.py +4 -0
- enapter-0.25.0/src/enapter/async_/generator.py +18 -0
- enapter-0.25.0/src/enapter/async_/routine.py +37 -0
- enapter-0.25.0/src/enapter/cli/__init__.py +6 -0
- enapter-0.25.0/src/enapter/cli/app.py +37 -0
- enapter-0.25.0/src/enapter/cli/command.py +16 -0
- enapter-0.25.0/src/enapter/cli/http/__init__.py +3 -0
- enapter-0.25.0/src/enapter/cli/http/api/__init__.py +3 -0
- enapter-0.25.0/src/enapter/cli/http/api/blueprint_command.py +43 -0
- enapter-0.25.0/src/enapter/cli/http/api/blueprint_download_command.py +41 -0
- enapter-0.25.0/src/enapter/cli/http/api/blueprint_get_command.py +23 -0
- enapter-0.25.0/src/enapter/cli/http/api/blueprint_profiles_command.py +29 -0
- enapter-0.25.0/src/enapter/cli/http/api/blueprint_profiles_download_command.py +31 -0
- enapter-0.25.0/src/enapter/cli/http/api/blueprint_upload_command.py +29 -0
- enapter-0.25.0/src/enapter/cli/http/api/blueprint_validate_command.py +27 -0
- enapter-0.25.0/src/enapter/cli/http/api/command.py +47 -0
- enapter-0.25.0/src/enapter/cli/http/api/command_arguments.py +11 -0
- enapter-0.25.0/src/enapter/cli/http/api/command_command.py +39 -0
- enapter-0.25.0/src/enapter/cli/http/api/command_create_execution_command.py +39 -0
- enapter-0.25.0/src/enapter/cli/http/api/command_execute_command.py +48 -0
- enapter-0.25.0/src/enapter/cli/http/api/command_get_execution_command.py +41 -0
- enapter-0.25.0/src/enapter/cli/http/api/command_list_executions_command.py +99 -0
- enapter-0.25.0/src/enapter/cli/http/api/device_assign_blueprint_command.py +28 -0
- enapter-0.25.0/src/enapter/cli/http/api/device_command.py +61 -0
- enapter-0.25.0/src/enapter/cli/http/api/device_create_lua_command.py +32 -0
- enapter-0.25.0/src/enapter/cli/http/api/device_create_standalone_command.py +26 -0
- enapter-0.25.0/src/enapter/cli/http/api/device_create_vucm_command.py +24 -0
- enapter-0.25.0/src/enapter/cli/http/api/device_delete_command.py +27 -0
- enapter-0.25.0/src/enapter/cli/http/api/device_generate_communication_config_command.py +33 -0
- enapter-0.25.0/src/enapter/cli/http/api/device_get_command.py +59 -0
- enapter-0.25.0/src/enapter/cli/http/api/device_list_command.py +72 -0
- enapter-0.25.0/src/enapter/cli/http/api/device_update_command.py +28 -0
- enapter-0.25.0/src/enapter/cli/http/api/rule_command.py +60 -0
- enapter-0.25.0/src/enapter/cli/http/api/rule_create_command.py +61 -0
- enapter-0.25.0/src/enapter/cli/http/api/rule_delete_command.py +24 -0
- enapter-0.25.0/src/enapter/cli/http/api/rule_disable_command.py +28 -0
- enapter-0.25.0/src/enapter/cli/http/api/rule_enable_command.py +28 -0
- enapter-0.25.0/src/enapter/cli/http/api/rule_engine_command.py +44 -0
- enapter-0.25.0/src/enapter/cli/http/api/rule_engine_get_command.py +25 -0
- enapter-0.25.0/src/enapter/cli/http/api/rule_engine_resume_command.py +25 -0
- enapter-0.25.0/src/enapter/cli/http/api/rule_engine_suspend_command.py +25 -0
- enapter-0.25.0/src/enapter/cli/http/api/rule_get_command.py +28 -0
- enapter-0.25.0/src/enapter/cli/http/api/rule_list_command.py +40 -0
- enapter-0.25.0/src/enapter/cli/http/api/rule_update_command.py +29 -0
- enapter-0.25.0/src/enapter/cli/http/api/rule_update_script_command.py +55 -0
- enapter-0.25.0/src/enapter/cli/http/api/site_command.py +43 -0
- enapter-0.25.0/src/enapter/cli/http/api/site_create_command.py +43 -0
- enapter-0.25.0/src/enapter/cli/http/api/site_delete_command.py +18 -0
- enapter-0.25.0/src/enapter/cli/http/api/site_get_command.py +22 -0
- enapter-0.25.0/src/enapter/cli/http/api/site_list_command.py +34 -0
- enapter-0.25.0/src/enapter/cli/http/api/site_location.py +11 -0
- enapter-0.25.0/src/enapter/cli/http/api/site_update_command.py +45 -0
- enapter-0.25.0/src/enapter/cli/http/api/telemetry_command.py +31 -0
- enapter-0.25.0/src/enapter/cli/http/api/telemetry_latest_command.py +36 -0
- enapter-0.25.0/src/enapter/cli/http/api/telemetry_timeseries_command.py +130 -0
- enapter-0.25.0/src/enapter/cli/http/command.py +27 -0
- enapter-0.25.0/src/enapter/cli/subparsers.py +8 -0
- enapter-0.25.0/src/enapter/cli/version_command.py +14 -0
- enapter-0.25.0/src/enapter/http/__init__.py +3 -0
- enapter-0.25.0/src/enapter/http/api/__init__.py +24 -0
- enapter-0.25.0/src/enapter/http/api/authentication.py +19 -0
- enapter-0.25.0/src/enapter/http/api/authorization.py +13 -0
- enapter-0.25.0/src/enapter/http/api/blueprints/__init__.py +8 -0
- enapter-0.25.0/src/enapter/http/api/blueprints/blueprint.py +30 -0
- enapter-0.25.0/src/enapter/http/api/blueprints/client.py +76 -0
- enapter-0.25.0/src/enapter/http/api/client.py +79 -0
- enapter-0.25.0/src/enapter/http/api/commands/__init__.py +14 -0
- enapter-0.25.0/src/enapter/http/api/commands/client.py +129 -0
- enapter-0.25.0/src/enapter/http/api/commands/execution.py +68 -0
- enapter-0.25.0/src/enapter/http/api/commands/request.py +25 -0
- enapter-0.25.0/src/enapter/http/api/commands/response.py +35 -0
- enapter-0.25.0/src/enapter/http/api/config.py +27 -0
- enapter-0.25.0/src/enapter/http/api/devices/__init__.py +25 -0
- enapter-0.25.0/src/enapter/http/api/devices/client.py +188 -0
- enapter-0.25.0/src/enapter/http/api/devices/communication_config.py +58 -0
- enapter-0.25.0/src/enapter/http/api/devices/device.py +74 -0
- enapter-0.25.0/src/enapter/http/api/devices/device_communication.py +39 -0
- enapter-0.25.0/src/enapter/http/api/devices/device_connectivity.py +22 -0
- enapter-0.25.0/src/enapter/http/api/devices/device_type.py +15 -0
- enapter-0.25.0/src/enapter/http/api/devices/mqtt_credentials.py +16 -0
- enapter-0.25.0/src/enapter/http/api/devices/mqtt_protocol.py +7 -0
- enapter-0.25.0/src/enapter/http/api/devices/mqtts_credentials.py +25 -0
- enapter-0.25.0/src/enapter/http/api/devices/time_sync_protocol.py +7 -0
- enapter-0.25.0/src/enapter/http/api/errors.py +50 -0
- enapter-0.25.0/src/enapter/http/api/pagination.py +54 -0
- enapter-0.25.0/src/enapter/http/api/rule_engine/__init__.py +19 -0
- enapter-0.25.0/src/enapter/http/api/rule_engine/client.py +195 -0
- enapter-0.25.0/src/enapter/http/api/rule_engine/engine.py +32 -0
- enapter-0.25.0/src/enapter/http/api/rule_engine/engine_state.py +10 -0
- enapter-0.25.0/src/enapter/http/api/rule_engine/rule.py +39 -0
- enapter-0.25.0/src/enapter/http/api/rule_engine/rule_script.py +37 -0
- enapter-0.25.0/src/enapter/http/api/rule_engine/rule_state.py +10 -0
- enapter-0.25.0/src/enapter/http/api/rule_engine/runtime_version.py +10 -0
- enapter-0.25.0/src/enapter/http/api/sites/__init__.py +9 -0
- enapter-0.25.0/src/enapter/http/api/sites/client.py +76 -0
- enapter-0.25.0/src/enapter/http/api/sites/location.py +25 -0
- enapter-0.25.0/src/enapter/http/api/sites/site.py +42 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/__init__.py +33 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/aggregation.py +11 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/client.py +105 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/data_type.py +31 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/gap_filling.py +18 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/gap_filling_method.py +7 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/labels.py +17 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/latest_datapoint.py +25 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/long_timeseries_row.py +18 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/long_timeseries_stream.py +34 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/raw_timeseries_row.py +10 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/raw_timeseries_stream.py +56 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/selector.py +34 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/wide_timeseries.py +33 -0
- enapter-0.25.0/src/enapter/http/api/telemetry/wide_timeseries_column.py +23 -0
- enapter-0.25.0/src/enapter/http/api/transport.py +5 -0
- enapter-0.25.0/src/enapter/log/__init__.py +34 -0
- enapter-0.25.0/src/enapter/log/json_formatter.py +36 -0
- enapter-0.25.0/src/enapter/mdns/__init__.py +3 -0
- enapter-0.25.0/src/enapter/mdns/resolver.py +43 -0
- enapter-0.25.0/src/enapter/mqtt/__init__.py +7 -0
- enapter-0.25.0/src/enapter/mqtt/api/__init__.py +6 -0
- enapter-0.25.0/src/enapter/mqtt/api/client.py +64 -0
- enapter-0.25.0/src/enapter/mqtt/api/config.py +67 -0
- enapter-0.25.0/src/enapter/mqtt/api/device/__init__.py +21 -0
- enapter-0.25.0/src/enapter/mqtt/api/device/channel.py +55 -0
- enapter-0.25.0/src/enapter/mqtt/api/device/command_request.py +38 -0
- enapter-0.25.0/src/enapter/mqtt/api/device/command_response.py +28 -0
- enapter-0.25.0/src/enapter/mqtt/api/device/command_state.py +8 -0
- enapter-0.25.0/src/enapter/mqtt/api/device/log.py +31 -0
- enapter-0.25.0/src/enapter/mqtt/api/device/log_severity.py +9 -0
- enapter-0.25.0/src/enapter/mqtt/api/device/message.py +24 -0
- enapter-0.25.0/src/enapter/mqtt/api/device/properties.py +24 -0
- enapter-0.25.0/src/enapter/mqtt/api/device/telemetry.py +28 -0
- enapter-0.25.0/src/enapter/mqtt/client.py +127 -0
- enapter-0.25.0/src/enapter/mqtt/errors.py +3 -0
- enapter-0.25.0/src/enapter/mqtt/message.py +3 -0
- enapter-0.25.0/src/enapter/py.typed +0 -0
- enapter-0.25.0/src/enapter/standalone/__init__.py +35 -0
- enapter-0.25.0/src/enapter/standalone/config.py +218 -0
- enapter-0.25.0/src/enapter/standalone/device.py +59 -0
- enapter-0.25.0/src/enapter/standalone/device_protocol.py +33 -0
- enapter-0.25.0/src/enapter/standalone/logger.py +21 -0
- enapter-0.25.0/src/enapter/standalone/mqtt_adapter.py +142 -0
- enapter-0.25.0/src/enapter/standalone/run.py +39 -0
- enapter-0.25.0/src/enapter/standalone/ucm.py +28 -0
- enapter-0.25.0/src/enapter.egg-info/PKG-INFO +70 -0
- enapter-0.25.0/src/enapter.egg-info/SOURCES.txt +153 -0
- enapter-0.25.0/src/enapter.egg-info/dependency_links.txt +1 -0
- enapter-0.25.0/src/enapter.egg-info/requires.txt +4 -0
- enapter-0.25.0/src/enapter.egg-info/top_level.txt +1 -0
enapter-0.25.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022-2026 Enapter
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
enapter-0.25.0/PKG-INFO
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: enapter
|
|
3
|
+
Version: 0.25.0
|
|
4
|
+
Summary: Enapter Python SDK
|
|
5
|
+
Author-email: Roman Novatorov <rnovatorov@enapter.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Enapter/python-sdk
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: aiomqtt==2.*
|
|
12
|
+
Requires-Dist: dnspython==2.*
|
|
13
|
+
Requires-Dist: json-log-formatter==1.*
|
|
14
|
+
Requires-Dist: httpx==0.28.*
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# Enapter Python SDK
|
|
18
|
+
|
|
19
|
+
[](https://github.com/Enapter/python-sdk/actions/workflows/ci.yml)
|
|
20
|
+
[](https://pypi.org/project/enapter)
|
|
21
|
+
[](https://github.com/python/black)
|
|
22
|
+
|
|
23
|
+
A software development kit (SDK) for building applications and integrations
|
|
24
|
+
with Enapter using Python.
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- [Standalone
|
|
29
|
+
Devices](https://v3.developers.enapter.com/docs/standalone/introduction)
|
|
30
|
+
framework
|
|
31
|
+
- [MQTT
|
|
32
|
+
API](https://v3.developers.enapter.com/reference/device_integration/mqtt_api/)
|
|
33
|
+
client
|
|
34
|
+
- [HTTP API](https://v3.developers.enapter.com/reference/http/intro) client
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
> [!IMPORTANT]
|
|
39
|
+
> Requires **Python 3.11+**.
|
|
40
|
+
|
|
41
|
+
> [!WARNING]
|
|
42
|
+
> The API is still under development and may change at any time. It is
|
|
43
|
+
> recommended to pin the package version when installing.
|
|
44
|
+
|
|
45
|
+
Install from PyPI:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install enapter==0.25.0
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
Explore the examples:
|
|
54
|
+
|
|
55
|
+
- [Standalone Devices](examples/standalone)
|
|
56
|
+
- [MQTT API](examples/mqtt/api)
|
|
57
|
+
- [HTTP API](examples/http/api)
|
|
58
|
+
|
|
59
|
+
These provide a good overview of the available features and should give you
|
|
60
|
+
enough to get started.
|
|
61
|
+
|
|
62
|
+
> [!TIP]
|
|
63
|
+
> Don't hesitate to peek into the source code - it's meant to be easy to
|
|
64
|
+
> follow.
|
|
65
|
+
|
|
66
|
+
## Help
|
|
67
|
+
|
|
68
|
+
If you feel lost or confused, reach us on
|
|
69
|
+
[Discord](https://discord.com/invite/TCaEZs3qpe) or just [file a
|
|
70
|
+
bug](https://github.com/Enapter/python-sdk/issues/new). We'd be glad to help.
|
enapter-0.25.0/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Enapter Python SDK
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Enapter/python-sdk/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/enapter)
|
|
5
|
+
[](https://github.com/python/black)
|
|
6
|
+
|
|
7
|
+
A software development kit (SDK) for building applications and integrations
|
|
8
|
+
with Enapter using Python.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- [Standalone
|
|
13
|
+
Devices](https://v3.developers.enapter.com/docs/standalone/introduction)
|
|
14
|
+
framework
|
|
15
|
+
- [MQTT
|
|
16
|
+
API](https://v3.developers.enapter.com/reference/device_integration/mqtt_api/)
|
|
17
|
+
client
|
|
18
|
+
- [HTTP API](https://v3.developers.enapter.com/reference/http/intro) client
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
> [!IMPORTANT]
|
|
23
|
+
> Requires **Python 3.11+**.
|
|
24
|
+
|
|
25
|
+
> [!WARNING]
|
|
26
|
+
> The API is still under development and may change at any time. It is
|
|
27
|
+
> recommended to pin the package version when installing.
|
|
28
|
+
|
|
29
|
+
Install from PyPI:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install enapter==0.25.0
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
Explore the examples:
|
|
38
|
+
|
|
39
|
+
- [Standalone Devices](examples/standalone)
|
|
40
|
+
- [MQTT API](examples/mqtt/api)
|
|
41
|
+
- [HTTP API](examples/http/api)
|
|
42
|
+
|
|
43
|
+
These provide a good overview of the available features and should give you
|
|
44
|
+
enough to get started.
|
|
45
|
+
|
|
46
|
+
> [!TIP]
|
|
47
|
+
> Don't hesitate to peek into the source code - it's meant to be easy to
|
|
48
|
+
> follow.
|
|
49
|
+
|
|
50
|
+
## Help
|
|
51
|
+
|
|
52
|
+
If you feel lost or confused, reach us on
|
|
53
|
+
[Discord](https://discord.com/invite/TCaEZs3qpe) or just [file a
|
|
54
|
+
bug](https://github.com/Enapter/python-sdk/issues/new). We'd be glad to help.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "enapter"
|
|
3
|
+
description = "Enapter Python SDK"
|
|
4
|
+
readme = "README.md"
|
|
5
|
+
license = "MIT"
|
|
6
|
+
license-files = ["LICENSE"]
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Roman Novatorov", email = "rnovatorov@enapter.com" },
|
|
10
|
+
]
|
|
11
|
+
dependencies = [
|
|
12
|
+
"aiomqtt==2.*",
|
|
13
|
+
"dnspython==2.*",
|
|
14
|
+
"json-log-formatter==1.*",
|
|
15
|
+
"httpx==0.28.*",
|
|
16
|
+
]
|
|
17
|
+
dynamic = ["version"]
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Homepage = "https://github.com/Enapter/python-sdk"
|
|
21
|
+
|
|
22
|
+
[dependency-groups]
|
|
23
|
+
dev = [
|
|
24
|
+
"black",
|
|
25
|
+
"docker",
|
|
26
|
+
"isort",
|
|
27
|
+
"mypy",
|
|
28
|
+
"pyflakes",
|
|
29
|
+
"pytest",
|
|
30
|
+
"pytest-asyncio",
|
|
31
|
+
"pytest-cov",
|
|
32
|
+
"types-docker",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[build-system]
|
|
36
|
+
requires = ["setuptools>=77"]
|
|
37
|
+
build-backend = "setuptools.build_meta"
|
|
38
|
+
|
|
39
|
+
[tool.setuptools]
|
|
40
|
+
package-dir = {"" = "src"}
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.packages.find]
|
|
43
|
+
where = ["src"]
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.dynamic]
|
|
46
|
+
version = { attr = "enapter.__version__" }
|
|
47
|
+
|
|
48
|
+
[tool.isort]
|
|
49
|
+
profile = "black"
|
|
50
|
+
known_first_party = ["enapter"]
|
|
51
|
+
|
|
52
|
+
[tool.pytest.ini_options]
|
|
53
|
+
asyncio_mode = "auto"
|
enapter-0.25.0/setup.cfg
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import functools
|
|
3
|
+
from typing import AsyncContextManager, AsyncGenerator, Callable
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def generator(
|
|
7
|
+
func: Callable[..., AsyncGenerator],
|
|
8
|
+
) -> Callable[..., AsyncContextManager[AsyncGenerator]]:
|
|
9
|
+
@functools.wraps(func)
|
|
10
|
+
@contextlib.asynccontextmanager
|
|
11
|
+
async def wrapper(*args, **kwargs) -> AsyncGenerator[AsyncGenerator, None]:
|
|
12
|
+
gen = func(*args, **kwargs)
|
|
13
|
+
try:
|
|
14
|
+
yield gen
|
|
15
|
+
finally:
|
|
16
|
+
await gen.aclose()
|
|
17
|
+
|
|
18
|
+
return wrapper
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import asyncio
|
|
3
|
+
import contextlib
|
|
4
|
+
from typing import Self
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Routine(abc.ABC):
|
|
8
|
+
|
|
9
|
+
def __init__(self, task_group: asyncio.TaskGroup | None = None) -> None:
|
|
10
|
+
self._task_group = task_group
|
|
11
|
+
self._task: asyncio.Task | None = None
|
|
12
|
+
|
|
13
|
+
@abc.abstractmethod
|
|
14
|
+
async def _run(self) -> None:
|
|
15
|
+
pass # pragma: no cover
|
|
16
|
+
|
|
17
|
+
async def __aenter__(self) -> Self:
|
|
18
|
+
await self.start()
|
|
19
|
+
return self
|
|
20
|
+
|
|
21
|
+
async def __aexit__(self, *_) -> None:
|
|
22
|
+
await self.stop()
|
|
23
|
+
|
|
24
|
+
async def start(self) -> None:
|
|
25
|
+
if self._task is not None:
|
|
26
|
+
raise RuntimeError("already started")
|
|
27
|
+
if self._task_group is None:
|
|
28
|
+
self._task = asyncio.create_task(self._run())
|
|
29
|
+
else:
|
|
30
|
+
self._task = self._task_group.create_task(self._run())
|
|
31
|
+
|
|
32
|
+
async def stop(self) -> None:
|
|
33
|
+
if self._task is None:
|
|
34
|
+
raise RuntimeError("not started yet")
|
|
35
|
+
self._task.cancel()
|
|
36
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
37
|
+
await self._task
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from enapter import log
|
|
5
|
+
|
|
6
|
+
from . import http
|
|
7
|
+
from .version_command import VersionCommand
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class App:
|
|
11
|
+
|
|
12
|
+
def __init__(self, args: argparse.Namespace) -> None:
|
|
13
|
+
self.args = args
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def new(cls) -> "App":
|
|
17
|
+
parser = argparse.ArgumentParser(
|
|
18
|
+
prog="enapter", formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
19
|
+
)
|
|
20
|
+
parser.add_argument("-v", "--verbose", action="store_true")
|
|
21
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
22
|
+
for command in [
|
|
23
|
+
http.Command,
|
|
24
|
+
VersionCommand,
|
|
25
|
+
]:
|
|
26
|
+
command.register(subparsers)
|
|
27
|
+
return cls(args=parser.parse_args())
|
|
28
|
+
|
|
29
|
+
async def run(self) -> None:
|
|
30
|
+
log.configure(level=logging.DEBUG if self.args.verbose else logging.INFO)
|
|
31
|
+
match self.args.command:
|
|
32
|
+
case "http":
|
|
33
|
+
await http.Command.run(self.args)
|
|
34
|
+
case "version":
|
|
35
|
+
await VersionCommand.run(self.args)
|
|
36
|
+
case _:
|
|
37
|
+
raise NotImplementedError(self.args.command)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import argparse
|
|
3
|
+
|
|
4
|
+
from .subparsers import Subparsers
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Command(abc.ABC):
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def register(subparsers: Subparsers) -> None:
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
@abc.abstractmethod
|
|
15
|
+
async def run(args: argparse.Namespace) -> None:
|
|
16
|
+
pass
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from enapter import cli
|
|
4
|
+
|
|
5
|
+
from .blueprint_download_command import BlueprintDownloadCommand
|
|
6
|
+
from .blueprint_get_command import BlueprintGetCommand
|
|
7
|
+
from .blueprint_profiles_command import BlueprintProfilesCommand
|
|
8
|
+
from .blueprint_upload_command import BlueprintUploadCommand
|
|
9
|
+
from .blueprint_validate_command import BlueprintValidateCommand
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BlueprintCommand(cli.Command):
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def register(parent: cli.Subparsers) -> None:
|
|
16
|
+
parser = parent.add_parser(
|
|
17
|
+
"blueprint", formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
18
|
+
)
|
|
19
|
+
subparsers = parser.add_subparsers(dest="blueprint_command", required=True)
|
|
20
|
+
for command in [
|
|
21
|
+
BlueprintDownloadCommand,
|
|
22
|
+
BlueprintGetCommand,
|
|
23
|
+
BlueprintProfilesCommand,
|
|
24
|
+
BlueprintUploadCommand,
|
|
25
|
+
BlueprintValidateCommand,
|
|
26
|
+
]:
|
|
27
|
+
command.register(subparsers)
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
async def run(args: argparse.Namespace) -> None:
|
|
31
|
+
match args.blueprint_command:
|
|
32
|
+
case "download":
|
|
33
|
+
await BlueprintDownloadCommand.run(args)
|
|
34
|
+
case "get":
|
|
35
|
+
await BlueprintGetCommand.run(args)
|
|
36
|
+
case "profiles":
|
|
37
|
+
await BlueprintProfilesCommand.run(args)
|
|
38
|
+
case "upload":
|
|
39
|
+
await BlueprintUploadCommand.run(args)
|
|
40
|
+
case "validate":
|
|
41
|
+
await BlueprintValidateCommand.run(args)
|
|
42
|
+
case _:
|
|
43
|
+
raise NotImplementedError(args.blueprint_command)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
import pathlib
|
|
4
|
+
|
|
5
|
+
from enapter import cli, http
|
|
6
|
+
|
|
7
|
+
LOGGER = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BlueprintDownloadCommand(cli.Command):
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def register(parent: cli.Subparsers) -> None:
|
|
14
|
+
parser = parent.add_parser(
|
|
15
|
+
"download", formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
16
|
+
)
|
|
17
|
+
parser.add_argument("id", help="ID of the blueprint to download")
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"-o",
|
|
20
|
+
"--output",
|
|
21
|
+
type=pathlib.Path,
|
|
22
|
+
help="Output directory path",
|
|
23
|
+
default=pathlib.Path.cwd(),
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"-v",
|
|
27
|
+
"--view",
|
|
28
|
+
choices=["original", "compiled"],
|
|
29
|
+
default="original",
|
|
30
|
+
help="Blueprint view type",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
async def run(args: argparse.Namespace) -> None:
|
|
35
|
+
async with http.api.Client(http.api.Config.from_env()) as client:
|
|
36
|
+
content = await client.blueprints.download(
|
|
37
|
+
args.id, view=http.api.blueprints.BlueprintView(args.view.upper())
|
|
38
|
+
)
|
|
39
|
+
output_path = args.output / f"{args.id}.zip"
|
|
40
|
+
with output_path.open("wb") as f:
|
|
41
|
+
f.write(content)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from enapter import cli, http
|
|
6
|
+
|
|
7
|
+
LOGGER = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BlueprintGetCommand(cli.Command):
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def register(parent: cli.Subparsers) -> None:
|
|
14
|
+
parser = parent.add_parser(
|
|
15
|
+
"get", formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
16
|
+
)
|
|
17
|
+
parser.add_argument("id", help="ID of the blueprint to get")
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
async def run(args: argparse.Namespace) -> None:
|
|
21
|
+
async with http.api.Client(http.api.Config.from_env()) as client:
|
|
22
|
+
blueprint = await client.blueprints.get(blueprint_id=args.id)
|
|
23
|
+
print(json.dumps(blueprint.to_dto()))
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from enapter import cli
|
|
4
|
+
|
|
5
|
+
from .blueprint_profiles_download_command import BlueprintProfilesDownloadCommand
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BlueprintProfilesCommand(cli.Command):
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def register(parent: cli.Subparsers) -> None:
|
|
12
|
+
parser = parent.add_parser(
|
|
13
|
+
"profiles", formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
14
|
+
)
|
|
15
|
+
subparsers = parser.add_subparsers(
|
|
16
|
+
dest="blueprint_profiles_command", required=True
|
|
17
|
+
)
|
|
18
|
+
for command in [
|
|
19
|
+
BlueprintProfilesDownloadCommand,
|
|
20
|
+
]:
|
|
21
|
+
command.register(subparsers)
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
async def run(args: argparse.Namespace) -> None:
|
|
25
|
+
match args.blueprint_profiles_command:
|
|
26
|
+
case "download":
|
|
27
|
+
await BlueprintProfilesDownloadCommand.run(args)
|
|
28
|
+
case _:
|
|
29
|
+
raise NotImplementedError(args.blueprint_profiles_command)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
import pathlib
|
|
4
|
+
|
|
5
|
+
from enapter import cli, http
|
|
6
|
+
|
|
7
|
+
LOGGER = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BlueprintProfilesDownloadCommand(cli.Command):
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def register(parent: cli.Subparsers) -> None:
|
|
14
|
+
parser = parent.add_parser(
|
|
15
|
+
"download", formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
16
|
+
)
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
"-o",
|
|
19
|
+
"--output",
|
|
20
|
+
type=pathlib.Path,
|
|
21
|
+
help="Output directory path",
|
|
22
|
+
default=pathlib.Path.cwd(),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
async def run(args: argparse.Namespace) -> None:
|
|
27
|
+
async with http.api.Client(http.api.Config.from_env()) as client:
|
|
28
|
+
content = await client.blueprints.download_device_profiles()
|
|
29
|
+
output_path = args.output / "device_profiles.zip"
|
|
30
|
+
with output_path.open("wb") as f:
|
|
31
|
+
f.write(content)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import pathlib
|
|
5
|
+
|
|
6
|
+
from enapter import cli, http
|
|
7
|
+
|
|
8
|
+
LOGGER = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BlueprintUploadCommand(cli.Command):
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def register(parent: cli.Subparsers) -> None:
|
|
15
|
+
parser = parent.add_parser(
|
|
16
|
+
"upload", formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
17
|
+
)
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"path", type=pathlib.Path, help="Path to a directory or a zip file"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
async def run(args: argparse.Namespace) -> None:
|
|
24
|
+
async with http.api.Client(http.api.Config.from_env()) as client:
|
|
25
|
+
if args.path.is_dir():
|
|
26
|
+
blueprint = await client.blueprints.upload_directory(args.path)
|
|
27
|
+
else:
|
|
28
|
+
blueprint = await client.blueprints.upload_file(args.path)
|
|
29
|
+
print(json.dumps(blueprint.to_dto()))
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
import pathlib
|
|
4
|
+
|
|
5
|
+
from enapter import cli, http
|
|
6
|
+
|
|
7
|
+
LOGGER = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BlueprintValidateCommand(cli.Command):
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def register(parent: cli.Subparsers) -> None:
|
|
14
|
+
parser = parent.add_parser(
|
|
15
|
+
"validate", formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
16
|
+
)
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
"path", type=pathlib.Path, help="Path to a directory or a zip file"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
async def run(args: argparse.Namespace) -> None:
|
|
23
|
+
async with http.api.Client(http.api.Config.from_env()) as client:
|
|
24
|
+
if args.path.is_dir():
|
|
25
|
+
await client.blueprints.validate_directory(args.path)
|
|
26
|
+
else:
|
|
27
|
+
await client.blueprints.validate_file(args.path)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from enapter import cli
|
|
4
|
+
|
|
5
|
+
from .blueprint_command import BlueprintCommand
|
|
6
|
+
from .command_command import CommandCommand
|
|
7
|
+
from .device_command import DeviceCommand
|
|
8
|
+
from .rule_engine_command import RuleEngineCommand
|
|
9
|
+
from .site_command import SiteCommand
|
|
10
|
+
from .telemetry_command import TelemetryCommand
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Command(cli.Command):
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def register(parent: cli.Subparsers) -> None:
|
|
17
|
+
parser = parent.add_parser(
|
|
18
|
+
"api", formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
19
|
+
)
|
|
20
|
+
subparsers = parser.add_subparsers(dest="api_command", required=True)
|
|
21
|
+
for command in [
|
|
22
|
+
BlueprintCommand,
|
|
23
|
+
CommandCommand,
|
|
24
|
+
DeviceCommand,
|
|
25
|
+
RuleEngineCommand,
|
|
26
|
+
SiteCommand,
|
|
27
|
+
TelemetryCommand,
|
|
28
|
+
]:
|
|
29
|
+
command.register(subparsers)
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
async def run(args: argparse.Namespace) -> None:
|
|
33
|
+
match args.api_command:
|
|
34
|
+
case "blueprint":
|
|
35
|
+
await BlueprintCommand.run(args)
|
|
36
|
+
case "command":
|
|
37
|
+
await CommandCommand.run(args)
|
|
38
|
+
case "device":
|
|
39
|
+
await DeviceCommand.run(args)
|
|
40
|
+
case "rule-engine":
|
|
41
|
+
await RuleEngineCommand.run(args)
|
|
42
|
+
case "site":
|
|
43
|
+
await SiteCommand.run(args)
|
|
44
|
+
case "telemetry":
|
|
45
|
+
await TelemetryCommand.run(args)
|
|
46
|
+
case _:
|
|
47
|
+
raise NotImplementedError(args.api_command)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def parse_command_arguments(arguments_string: str | None) -> dict:
|
|
6
|
+
if arguments_string is None:
|
|
7
|
+
return {}
|
|
8
|
+
try:
|
|
9
|
+
return json.loads(arguments_string)
|
|
10
|
+
except json.JSONDecodeError as e:
|
|
11
|
+
raise argparse.ArgumentTypeError(f"Decode JSON: {e.msg}")
|