firmngin 0.0.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.
firmngin-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Firmngin
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.
@@ -0,0 +1,194 @@
1
+ Metadata-Version: 2.4
2
+ Name: firmngin
3
+ Version: 0.0.1
4
+ Summary: Python SDK for building Firmngin device integrations
5
+ Author-email: Firmngin <arif@firmngin.dev>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/firmngin/firmngin-sdk-python
8
+ Project-URL: Repository, https://github.com/firmngin/firmngin-sdk-python
9
+ Project-URL: Issues, https://github.com/firmngin/firmngin-sdk-python/issues
10
+ Project-URL: Changelog, https://github.com/firmngin/firmngin-sdk-python/blob/main/CHANGELOG.md
11
+ Keywords: iot,firmngin,device,sdk,asyncio
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Communications
21
+ Classifier: Topic :: Software Development :: Embedded Systems
22
+ Classifier: Topic :: System :: Hardware
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: aiomqtt<3.0,>=2.0
28
+ Requires-Dist: cryptography<44.0,>=42.0
29
+ Provides-Extra: http
30
+ Requires-Dist: httpx<1.0,>=0.27; extra == "http"
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest>=8.0; extra == "dev"
33
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
34
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
35
+ Requires-Dist: respx>=0.21; extra == "dev"
36
+ Requires-Dist: httpx<1.0,>=0.27; extra == "dev"
37
+ Requires-Dist: mypy<2.0,>=1.13; extra == "dev"
38
+ Requires-Dist: ruff>=0.5; extra == "dev"
39
+ Requires-Dist: testcontainers>=4.7; extra == "dev"
40
+ Requires-Dist: pip-audit>=2.7; extra == "dev"
41
+ Requires-Dist: bandit>=1.7; extra == "dev"
42
+ Requires-Dist: build>=1.2; extra == "dev"
43
+ Requires-Dist: twine>=6.1; extra == "dev"
44
+ Requires-Dist: packaging>=24.2; extra == "dev"
45
+ Dynamic: license-file
46
+
47
+ # Firmngin Python SDK
48
+
49
+ <p align="center">
50
+ <img src="logo.png" alt="Firmngin Logo" width="96"/>
51
+ </p>
52
+
53
+ [![Python](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
54
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
55
+
56
+ Python SDK for [Firmngin](https://firmngin.dev) device clients, Raspberry Pi. Supports async and sync styles with mTLS, E2EE, entity state, payments, and optional image upload.
57
+
58
+ **Docs:** [Introduction](https://firmngin.dev/docs/libraries/raspi) — Python SDK menu on [firmngin.dev/docs](https://firmngin.dev/docs)
59
+
60
+ ## Install
61
+
62
+ ```bash
63
+ pip install firmngin
64
+ ```
65
+
66
+ Image upload (optional):
67
+
68
+ ```bash
69
+ pip install firmngin[http]
70
+ ```
71
+
72
+ From Git:
73
+
74
+ ```bash
75
+ pip install git+https://github.com/firmngin/firmngin-sdk-python.git
76
+ ```
77
+
78
+ ## Dependencies
79
+
80
+ Core install uses `aiomqtt` (MQTT) and `cryptography` (TLS + AES-GCM E2EE) only.
81
+
82
+ ## Client vs AsyncClient
83
+
84
+ | | `AsyncClient` | `Client` |
85
+ | ---------- | ------------------------------- | ------------------------- | --- |
86
+ | Style | `async` / `await` | Blocking calls | |
87
+ | Push state | `await client.push_entity(...)` | `client.push_entity(...)` |
88
+ | Event loop | `await client.run()` | `client.run()` |
89
+
90
+ Both use `ClientConfig.from_file("keys.json")` and support the same events.
91
+
92
+ ## Quickstart
93
+
94
+ ```python
95
+ from firmngin import Client, ClientConfig, Entity, EntityCommand
96
+
97
+ relay = Entity("relay")
98
+ temperature = Entity("temperature")
99
+
100
+ with Client(ClientConfig.from_file("keys.json")) as client:
101
+
102
+ @client.on_entity(relay)
103
+ def handle_relay(command: EntityCommand) -> None:
104
+ print("relay:", command.value)
105
+
106
+ client.connect()
107
+ client.push_entity(temperature, 27.5)
108
+ client.run()
109
+ ```
110
+
111
+ ## Debug mode
112
+
113
+ Debug is **off** by default — no banner or console output.
114
+
115
+ ```python
116
+ client.set_debug(True)
117
+ client.connect()
118
+ ```
119
+
120
+ With debug enabled, `connect()` prints the Firmngin banner, connection details, and connect/disconnect messages.
121
+
122
+ ## Events
123
+
124
+ Register handlers with `client.on(Event.X, handler)`:
125
+
126
+ | Event | Payload | Description |
127
+ | ------------------------ | --------------- | ---------------------- |
128
+ | `Event.PAYMENT` | `Payment` | Payment updates |
129
+ | `Event.PAYMENT_PENDING` | `Payment` | Payment pending |
130
+ | `Event.PAYMENT_SUCCESS` | `Payment` | Payment succeeded |
131
+ | `Event.INIT` | `Init` | Device init payload |
132
+ | `Event.MERCHANT_STATUS` | `str` | Merchant status string |
133
+ | `Event.VERIFICATION` | `Verification` | Verification result |
134
+ | `Event.PIN` | `Verification` | PIN display |
135
+ | `Event.DEVICE_STATUS` | `DeviceStatus` | Device lifecycle |
136
+ | `Event.ENTITY_COMMAND` | `EntityCommand` | Any entity command |
137
+ | `Event.ACTIVE_SESSION` | `ActiveSession` | Active paid session |
138
+ | `Event.METADATA_PENDING` | `str` | Raw JSON from `mop` |
139
+ | `Event.METADATA_EXPIRED` | `str` | Raw JSON from `moe` |
140
+ | `Event.METADATA_SUCCESS` | `str` | Raw JSON from `mos` |
141
+ | `Event.ERROR` | `Exception` | Runtime error |
142
+
143
+ Metadata events pass the decrypted payload as a **JSON string** — parse it in your handler.
144
+
145
+ ```python
146
+ import json
147
+
148
+ @client.on(Event.METADATA_PENDING)
149
+ def on_metadata(raw_json: str) -> None:
150
+ data = json.loads(raw_json)
151
+ ```
152
+
153
+ Per-entity commands:
154
+
155
+ ```python
156
+ @client.on_entity(Entity("relay"))
157
+ def on_relay(command: EntityCommand) -> None:
158
+ print(command.value)
159
+ ```
160
+
161
+ ## API overview
162
+
163
+ | Import | Role |
164
+ | ------------------------------ | -------------------------------- |
165
+ | `AsyncClient` | Async device client |
166
+ | `Client` | Sync blocking client |
167
+ | `ClientConfig` | Runtime settings |
168
+ | `KeysConfig` | `keys.json` loader |
169
+ | `Event` | Event names for `client.on(...)` |
170
+ | `Entity`, `Payment`, `Init`, … | Typed payloads |
171
+
172
+ ## Device keys
173
+
174
+ Download `keys.json` from the Firmngin dashboard. The SDK accepts dashboard fields including `server_fingerprint_bytes` (20-byte Arduino-style fingerprint) and picks the right TLS validation mode automatically.
175
+
176
+ Never commit real keys. See `keys.json.example`.
177
+
178
+ ## Image upload
179
+
180
+ ```bash
181
+ pip install firmngin[http]
182
+ ```
183
+
184
+ ```python
185
+ await client.upload_image(Entity("camera"), "snapshot.jpg")
186
+ ```
187
+
188
+ ## Examples
189
+
190
+ See [`examples/`](examples/).
191
+
192
+ ## License
193
+
194
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,148 @@
1
+ # Firmngin Python SDK
2
+
3
+ <p align="center">
4
+ <img src="logo.png" alt="Firmngin Logo" width="96"/>
5
+ </p>
6
+
7
+ [![Python](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
9
+
10
+ Python SDK for [Firmngin](https://firmngin.dev) device clients, Raspberry Pi. Supports async and sync styles with mTLS, E2EE, entity state, payments, and optional image upload.
11
+
12
+ **Docs:** [Introduction](https://firmngin.dev/docs/libraries/raspi) — Python SDK menu on [firmngin.dev/docs](https://firmngin.dev/docs)
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pip install firmngin
18
+ ```
19
+
20
+ Image upload (optional):
21
+
22
+ ```bash
23
+ pip install firmngin[http]
24
+ ```
25
+
26
+ From Git:
27
+
28
+ ```bash
29
+ pip install git+https://github.com/firmngin/firmngin-sdk-python.git
30
+ ```
31
+
32
+ ## Dependencies
33
+
34
+ Core install uses `aiomqtt` (MQTT) and `cryptography` (TLS + AES-GCM E2EE) only.
35
+
36
+ ## Client vs AsyncClient
37
+
38
+ | | `AsyncClient` | `Client` |
39
+ | ---------- | ------------------------------- | ------------------------- | --- |
40
+ | Style | `async` / `await` | Blocking calls | |
41
+ | Push state | `await client.push_entity(...)` | `client.push_entity(...)` |
42
+ | Event loop | `await client.run()` | `client.run()` |
43
+
44
+ Both use `ClientConfig.from_file("keys.json")` and support the same events.
45
+
46
+ ## Quickstart
47
+
48
+ ```python
49
+ from firmngin import Client, ClientConfig, Entity, EntityCommand
50
+
51
+ relay = Entity("relay")
52
+ temperature = Entity("temperature")
53
+
54
+ with Client(ClientConfig.from_file("keys.json")) as client:
55
+
56
+ @client.on_entity(relay)
57
+ def handle_relay(command: EntityCommand) -> None:
58
+ print("relay:", command.value)
59
+
60
+ client.connect()
61
+ client.push_entity(temperature, 27.5)
62
+ client.run()
63
+ ```
64
+
65
+ ## Debug mode
66
+
67
+ Debug is **off** by default — no banner or console output.
68
+
69
+ ```python
70
+ client.set_debug(True)
71
+ client.connect()
72
+ ```
73
+
74
+ With debug enabled, `connect()` prints the Firmngin banner, connection details, and connect/disconnect messages.
75
+
76
+ ## Events
77
+
78
+ Register handlers with `client.on(Event.X, handler)`:
79
+
80
+ | Event | Payload | Description |
81
+ | ------------------------ | --------------- | ---------------------- |
82
+ | `Event.PAYMENT` | `Payment` | Payment updates |
83
+ | `Event.PAYMENT_PENDING` | `Payment` | Payment pending |
84
+ | `Event.PAYMENT_SUCCESS` | `Payment` | Payment succeeded |
85
+ | `Event.INIT` | `Init` | Device init payload |
86
+ | `Event.MERCHANT_STATUS` | `str` | Merchant status string |
87
+ | `Event.VERIFICATION` | `Verification` | Verification result |
88
+ | `Event.PIN` | `Verification` | PIN display |
89
+ | `Event.DEVICE_STATUS` | `DeviceStatus` | Device lifecycle |
90
+ | `Event.ENTITY_COMMAND` | `EntityCommand` | Any entity command |
91
+ | `Event.ACTIVE_SESSION` | `ActiveSession` | Active paid session |
92
+ | `Event.METADATA_PENDING` | `str` | Raw JSON from `mop` |
93
+ | `Event.METADATA_EXPIRED` | `str` | Raw JSON from `moe` |
94
+ | `Event.METADATA_SUCCESS` | `str` | Raw JSON from `mos` |
95
+ | `Event.ERROR` | `Exception` | Runtime error |
96
+
97
+ Metadata events pass the decrypted payload as a **JSON string** — parse it in your handler.
98
+
99
+ ```python
100
+ import json
101
+
102
+ @client.on(Event.METADATA_PENDING)
103
+ def on_metadata(raw_json: str) -> None:
104
+ data = json.loads(raw_json)
105
+ ```
106
+
107
+ Per-entity commands:
108
+
109
+ ```python
110
+ @client.on_entity(Entity("relay"))
111
+ def on_relay(command: EntityCommand) -> None:
112
+ print(command.value)
113
+ ```
114
+
115
+ ## API overview
116
+
117
+ | Import | Role |
118
+ | ------------------------------ | -------------------------------- |
119
+ | `AsyncClient` | Async device client |
120
+ | `Client` | Sync blocking client |
121
+ | `ClientConfig` | Runtime settings |
122
+ | `KeysConfig` | `keys.json` loader |
123
+ | `Event` | Event names for `client.on(...)` |
124
+ | `Entity`, `Payment`, `Init`, … | Typed payloads |
125
+
126
+ ## Device keys
127
+
128
+ Download `keys.json` from the Firmngin dashboard. The SDK accepts dashboard fields including `server_fingerprint_bytes` (20-byte Arduino-style fingerprint) and picks the right TLS validation mode automatically.
129
+
130
+ Never commit real keys. See `keys.json.example`.
131
+
132
+ ## Image upload
133
+
134
+ ```bash
135
+ pip install firmngin[http]
136
+ ```
137
+
138
+ ```python
139
+ await client.upload_image(Entity("camera"), "snapshot.jpg")
140
+ ```
141
+
142
+ ## Examples
143
+
144
+ See [`examples/`](examples/).
145
+
146
+ ## License
147
+
148
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,191 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "firmngin"
7
+ version = "0.0.1"
8
+ description = "Python SDK for building Firmngin device integrations"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ authors = [
13
+ {name = "Firmngin", email = "arif@firmngin.dev"},
14
+ ]
15
+ keywords = [
16
+ "iot",
17
+ "firmngin",
18
+ "device",
19
+ "sdk",
20
+ "asyncio",
21
+ ]
22
+ classifiers = [
23
+ "Development Status :: 3 - Alpha",
24
+ "Intended Audience :: Developers",
25
+ "Operating System :: OS Independent",
26
+ "Programming Language :: Python :: 3",
27
+ "Programming Language :: Python :: 3.9",
28
+ "Programming Language :: Python :: 3.10",
29
+ "Programming Language :: Python :: 3.11",
30
+ "Programming Language :: Python :: 3.12",
31
+ "Topic :: Communications",
32
+ "Topic :: Software Development :: Embedded Systems",
33
+ "Topic :: System :: Hardware",
34
+ "Typing :: Typed",
35
+ ]
36
+ dependencies = [
37
+ "aiomqtt>=2.0,<3.0",
38
+ "cryptography>=42.0,<44.0",
39
+ ]
40
+
41
+ [project.optional-dependencies]
42
+ http = [
43
+ "httpx>=0.27,<1.0",
44
+ ]
45
+ dev = [
46
+ "pytest>=8.0",
47
+ "pytest-asyncio>=0.23",
48
+ "pytest-cov>=5.0",
49
+ "respx>=0.21",
50
+ "httpx>=0.27,<1.0",
51
+ # Pinned to <2 because mypy 2.x dropped support for python_version = "3.9".
52
+ # We target 3.9 runtime; once we raise minimum to 3.10, bump to mypy 2.x.
53
+ "mypy>=1.13,<2.0",
54
+ "ruff>=0.5",
55
+ "testcontainers>=4.7",
56
+ "pip-audit>=2.7",
57
+ "bandit>=1.7",
58
+ "build>=1.2",
59
+ "twine>=6.1",
60
+ "packaging>=24.2",
61
+ ]
62
+
63
+ [project.urls]
64
+ Homepage = "https://github.com/firmngin/firmngin-sdk-python"
65
+ Repository = "https://github.com/firmngin/firmngin-sdk-python"
66
+ Issues = "https://github.com/firmngin/firmngin-sdk-python/issues"
67
+ Changelog = "https://github.com/firmngin/firmngin-sdk-python/blob/main/CHANGELOG.md"
68
+
69
+ [tool.setuptools]
70
+ license-files = ["LICENSE"]
71
+
72
+ [tool.setuptools.packages.find]
73
+ where = ["src"]
74
+ include = ["firmngin*"]
75
+
76
+ [tool.setuptools.package-data]
77
+ firmngin = ["py.typed"]
78
+
79
+ # --------------------------------------------------------------------------------------
80
+ # Ruff — lint + format (replaces black + isort + flake8)
81
+ # --------------------------------------------------------------------------------------
82
+ [tool.ruff]
83
+ target-version = "py39"
84
+ line-length = 100
85
+ extend-exclude = [
86
+ ".venv",
87
+ "build",
88
+ "dist",
89
+ ".tox",
90
+ ]
91
+
92
+ [tool.ruff.lint]
93
+ # Strict-ish default; relax if needed with explicit per-file ignores.
94
+ select = [
95
+ "E", # pycodestyle errors
96
+ "W", # pycodestyle warnings
97
+ "F", # pyflakes
98
+ "I", # isort
99
+ "N", # pep8-naming
100
+ "UP", # pyupgrade
101
+ "B", # flake8-bugbear
102
+ "C4", # flake8-comprehensions
103
+ "SIM", # flake8-simplify
104
+ "TID", # flake8-tidy-imports
105
+ "PL", # pylint
106
+ "RUF", # ruff-specific
107
+ ]
108
+ ignore = [
109
+ "PLR0913", # too-many-arguments — frequent in async APIs, not a bug
110
+ "PLR2004", # magic value — fine for protocol constants
111
+ "E501", # line length handled by formatter
112
+ ]
113
+
114
+ [tool.ruff.lint.per-file-ignores]
115
+ "tests/*" = ["PLR0913", "PLR0915", "PLR2004", "N802", "N803", "N806", "S101", "B011"]
116
+ "examples/*" = ["TID252", "N802", "N803"]
117
+
118
+ [tool.ruff.lint.isort]
119
+ known-first-party = ["firmngin", "tests"]
120
+
121
+ [tool.ruff.format]
122
+ quote-style = "double"
123
+ indent-style = "space"
124
+
125
+ # --------------------------------------------------------------------------------------
126
+ # Mypy — strict typing on the public package
127
+ # --------------------------------------------------------------------------------------
128
+ [tool.mypy]
129
+ python_version = "3.9"
130
+ strict = true
131
+ warn_return_any = true
132
+ warn_unused_configs = true
133
+ disallow_untyped_defs = true
134
+ disallow_incomplete_defs = true
135
+ check_untyped_defs = true
136
+ no_implicit_optional = true
137
+ warn_redundant_casts = true
138
+ warn_unused_ignores = true
139
+ warn_no_return = true
140
+
141
+ [[tool.mypy.overrides]]
142
+ module = "tests.*"
143
+ disallow_untyped_defs = false
144
+
145
+ [[tool.mypy.overrides]]
146
+ module = "httpx"
147
+ ignore_missing_imports = true
148
+
149
+ # Third-party libs without full type stubs — added per-module as we import them
150
+ # in later phases. Keep this list empty for Phase 0; expand as needed.
151
+
152
+ # --------------------------------------------------------------------------------------
153
+ # Pytest
154
+ # --------------------------------------------------------------------------------------
155
+ [tool.pytest.ini_options]
156
+ minversion = "8.0"
157
+ addopts = [
158
+ "-ra",
159
+ "--strict-markers",
160
+ "--strict-config",
161
+ "--showlocals",
162
+ "--tb=short",
163
+ ]
164
+ testpaths = ["tests"]
165
+ asyncio_mode = "auto"
166
+ asyncio_default_fixture_loop_scope = "function"
167
+ markers = [
168
+ "integration: integration tests requiring a real broker (deselect with -m 'not integration')",
169
+ "slow: slow tests (deselect with -m 'not slow')",
170
+ ]
171
+
172
+ # --------------------------------------------------------------------------------------
173
+ # Coverage
174
+ # --------------------------------------------------------------------------------------
175
+ [tool.coverage.run]
176
+ branch = true
177
+ source = ["src/firmngin"]
178
+ omit = ["*/tests/*", "*/examples/*"]
179
+
180
+ [tool.coverage.report]
181
+ precision = 2
182
+ show_missing = true
183
+ skip_covered = false
184
+ exclude_lines = [
185
+ "pragma: no cover",
186
+ "raise NotImplementedError",
187
+ "if TYPE_CHECKING:",
188
+ "if __name__ == .__main__.:",
189
+ "\\.\\.\\.",
190
+ ]
191
+ fail_under = 90
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,42 @@
1
+ """Firmngin Python SDK."""
2
+
3
+ from firmngin._version import __version__
4
+ from firmngin.builders import BatchState, LocationUpdate
5
+ from firmngin.client import AsyncClient, Event, FirmnginClient
6
+ from firmngin.client_sync import Client, SyncClient, SyncFirmnginClient
7
+ from firmngin.config import ClientConfig, KeysConfig, ValidationMode
8
+ from firmngin.payloads import (
9
+ DeviceStatus,
10
+ Entity,
11
+ EntityCommand,
12
+ EntityValue,
13
+ Init,
14
+ Payment,
15
+ Usage,
16
+ Verification,
17
+ )
18
+ from firmngin.session import ActiveSession
19
+
20
+ __all__ = [
21
+ "ActiveSession",
22
+ "AsyncClient",
23
+ "BatchState",
24
+ "Client",
25
+ "ClientConfig",
26
+ "DeviceStatus",
27
+ "Entity",
28
+ "EntityCommand",
29
+ "EntityValue",
30
+ "Event",
31
+ "FirmnginClient",
32
+ "Init",
33
+ "KeysConfig",
34
+ "LocationUpdate",
35
+ "Payment",
36
+ "Usage",
37
+ "SyncClient",
38
+ "SyncFirmnginClient",
39
+ "ValidationMode",
40
+ "Verification",
41
+ "__version__",
42
+ ]
@@ -0,0 +1,25 @@
1
+ """Startup banner for Firmngin device clients."""
2
+
3
+ from __future__ import annotations
4
+
5
+ _BANNER_LINES = (
6
+ "",
7
+ r" __ _ _ _ ",
8
+ r" / _(_) (_) | | ",
9
+ r" | |_ _ _ __ _ __ ___ _ __ __ _ _ _ __ __| | _____ __",
10
+ r" | _| | '__| '_ ` _ \| '_ \ / _` | | '_ \ / _` |/ _ \ \ / /",
11
+ r" | | | | | | | | | | | | | | (_| | | | | || (_| | __/\ V / ",
12
+ r" |_| |_|_| |_| |_| |_|_| |_|\__, |_|_| |_(_)__,_|\___| \_/ ",
13
+ r" __/ | ",
14
+ r" The AIoT Platform |___/ ",
15
+ )
16
+
17
+
18
+ def print_startup_banner(version: str) -> None:
19
+ for line in _BANNER_LINES:
20
+ print(line)
21
+ print(f" Version: {version}")
22
+ print()
23
+
24
+
25
+ __all__ = ["print_startup_banner"]