shellsmith 0.2.1__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. shellsmith-0.3.0/PKG-INFO +343 -0
  2. shellsmith-0.3.0/README.md +285 -0
  3. shellsmith-0.3.0/pyproject.toml +91 -0
  4. shellsmith-0.3.0/src/shellsmith/__init__.py +30 -0
  5. shellsmith-0.3.0/src/shellsmith/__main__.py +6 -0
  6. shellsmith-0.3.0/src/shellsmith/cli/app.py +44 -0
  7. shellsmith-0.3.0/src/shellsmith/cli/args.py +30 -0
  8. shellsmith-0.3.0/src/shellsmith/cli/commands/decode.py +13 -0
  9. shellsmith-0.3.0/src/shellsmith/cli/commands/encode.py +13 -0
  10. shellsmith-0.3.0/src/shellsmith/cli/commands/groups/__init__.py +0 -0
  11. shellsmith-0.3.0/src/shellsmith/cli/commands/groups/create.py +79 -0
  12. shellsmith-0.3.0/src/shellsmith/cli/commands/groups/delete.py +83 -0
  13. shellsmith-0.3.0/src/shellsmith/cli/commands/groups/get.py +165 -0
  14. shellsmith-0.3.0/src/shellsmith/cli/commands/groups/update.py +111 -0
  15. shellsmith-0.3.0/src/shellsmith/cli/commands/info.py +71 -0
  16. shellsmith-0.3.0/src/shellsmith/cli/commands/nuke.py +66 -0
  17. shellsmith-0.3.0/src/shellsmith/cli/commands/upload.py +32 -0
  18. shellsmith-0.3.0/src/shellsmith/cli/formats.py +13 -0
  19. shellsmith-0.3.0/src/shellsmith/cli/handlers.py +31 -0
  20. shellsmith-0.3.0/src/shellsmith/cli/opts.py +41 -0
  21. shellsmith-0.3.0/src/shellsmith/cli/pretty.py +142 -0
  22. shellsmith-0.3.0/src/shellsmith/cli/resolve.py +30 -0
  23. shellsmith-0.3.0/src/shellsmith/cli/simplify.py +74 -0
  24. shellsmith-0.3.0/src/shellsmith/config.py +41 -0
  25. shellsmith-0.3.0/src/shellsmith/crud/__init__.py +29 -0
  26. shellsmith-0.3.0/src/shellsmith/crud/shells.py +205 -0
  27. shellsmith-0.3.0/src/shellsmith/crud/submodels.py +434 -0
  28. shellsmith-0.3.0/src/shellsmith/extract.py +13 -0
  29. shellsmith-0.3.0/src/shellsmith/neo4j.py +163 -0
  30. shellsmith-0.3.0/src/shellsmith/services.py +191 -0
  31. shellsmith-0.3.0/src/shellsmith/upload.py +62 -0
  32. shellsmith-0.3.0/src/shellsmith/utils.py +95 -0
  33. shellsmith-0.3.0/src/shellsmith.egg-info/PKG-INFO +343 -0
  34. {shellsmith-0.2.1 → shellsmith-0.3.0}/src/shellsmith.egg-info/SOURCES.txt +17 -8
  35. shellsmith-0.3.0/src/shellsmith.egg-info/entry_points.txt +2 -0
  36. {shellsmith-0.2.1 → shellsmith-0.3.0}/src/shellsmith.egg-info/requires.txt +6 -3
  37. {shellsmith-0.2.1 → shellsmith-0.3.0}/tests/test_neo4j.py +0 -1
  38. shellsmith-0.3.0/tests/test_services.py +65 -0
  39. {shellsmith-0.2.1 → shellsmith-0.3.0}/tests/test_utils.py +13 -1
  40. shellsmith-0.2.1/PKG-INFO +0 -221
  41. shellsmith-0.2.1/README.md +0 -174
  42. shellsmith-0.2.1/pyproject.toml +0 -41
  43. shellsmith-0.2.1/src/shellsmith/__init__.py +0 -18
  44. shellsmith-0.2.1/src/shellsmith/__main__.py +0 -4
  45. shellsmith-0.2.1/src/shellsmith/cli/commands/__init__.py +0 -6
  46. shellsmith-0.2.1/src/shellsmith/cli/commands/info.py +0 -52
  47. shellsmith-0.2.1/src/shellsmith/cli/commands/nuke.py +0 -9
  48. shellsmith-0.2.1/src/shellsmith/cli/commands/shell.py +0 -17
  49. shellsmith-0.2.1/src/shellsmith/cli/commands/sme.py +0 -13
  50. shellsmith-0.2.1/src/shellsmith/cli/commands/submodel.py +0 -16
  51. shellsmith-0.2.1/src/shellsmith/cli/commands/upload.py +0 -14
  52. shellsmith-0.2.1/src/shellsmith/cli/main.py +0 -62
  53. shellsmith-0.2.1/src/shellsmith/cli/parser.py +0 -97
  54. shellsmith-0.2.1/src/shellsmith/config.py +0 -19
  55. shellsmith-0.2.1/src/shellsmith/crud/shells.py +0 -61
  56. shellsmith-0.2.1/src/shellsmith/crud/submodels.py +0 -126
  57. shellsmith-0.2.1/src/shellsmith/neo4j.py +0 -115
  58. shellsmith-0.2.1/src/shellsmith/services.py +0 -131
  59. shellsmith-0.2.1/src/shellsmith/upload.py +0 -40
  60. shellsmith-0.2.1/src/shellsmith/utils.py +0 -35
  61. shellsmith-0.2.1/src/shellsmith.egg-info/PKG-INFO +0 -221
  62. shellsmith-0.2.1/src/shellsmith.egg-info/entry_points.txt +0 -2
  63. shellsmith-0.2.1/tests/test_cli.py +0 -110
  64. shellsmith-0.2.1/tests/test_cli_integration.py +0 -59
  65. shellsmith-0.2.1/tests/test_crud.py +0 -123
  66. {shellsmith-0.2.1 → shellsmith-0.3.0}/LICENSE +0 -0
  67. {shellsmith-0.2.1 → shellsmith-0.3.0}/setup.cfg +0 -0
  68. {shellsmith-0.2.1 → shellsmith-0.3.0}/src/shellsmith/cli/__init__.py +0 -0
  69. {shellsmith-0.2.1/src/shellsmith/crud → shellsmith-0.3.0/src/shellsmith/cli/commands}/__init__.py +0 -0
  70. {shellsmith-0.2.1 → shellsmith-0.3.0}/src/shellsmith.egg-info/dependency_links.txt +0 -0
  71. {shellsmith-0.2.1 → shellsmith-0.3.0}/src/shellsmith.egg-info/top_level.txt +0 -0
  72. {shellsmith-0.2.1 → shellsmith-0.3.0}/tests/test_upload.py +0 -0
@@ -0,0 +1,343 @@
1
+ Metadata-Version: 2.4
2
+ Name: shellsmith
3
+ Version: 0.3.0
4
+ Summary: Python client and CLI for Eclipse BaSyx to manage Asset Administration Shells (AAS)
5
+ Author-email: Peter Stein <peterstein@dfki.de>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Peter Stein
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/ptrstn/shellsmith
29
+ Project-URL: Issues, https://github.com/ptrstn/shellsmith/issues
30
+ Keywords: aas,asset-administration-shell,basyx,eclipse-basyx,industry40,i40,digital-twin,cli,typer,rest-client,python
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: Programming Language :: Python :: 3.10
33
+ Classifier: Programming Language :: Python :: 3.11
34
+ Classifier: Programming Language :: Python :: 3.12
35
+ Classifier: Programming Language :: Python :: 3.13
36
+ Classifier: Operating System :: OS Independent
37
+ Classifier: Intended Audience :: Developers
38
+ Classifier: Topic :: Software Development :: Libraries
39
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
40
+ Requires-Python: >=3.10
41
+ Description-Content-Type: text/markdown
42
+ License-File: LICENSE
43
+ Requires-Dist: pydantic-settings
44
+ Requires-Dist: pyyaml
45
+ Requires-Dist: requests
46
+ Requires-Dist: typer
47
+ Provides-Extra: test
48
+ Requires-Dist: mkdocs; extra == "test"
49
+ Requires-Dist: mkdocs-material; extra == "test"
50
+ Requires-Dist: pytest; extra == "test"
51
+ Requires-Dist: pytest-cov; extra == "test"
52
+ Requires-Dist: pytest-dotenv; extra == "test"
53
+ Requires-Dist: ruff; extra == "test"
54
+ Requires-Dist: termynal; extra == "test"
55
+ Provides-Extra: neo4j
56
+ Requires-Dist: neo4j; extra == "neo4j"
57
+ Dynamic: license-file
58
+
59
+ <div align="center">
60
+ <img src="docs/images/logo-purple.png" alt="shellsmith" style="max-width: 100%; width: 600px;">
61
+ </div>
62
+
63
+ <div align="center">
64
+ <a href="https://github.com/ptrstn/shellsmith/actions/workflows/test.yaml"><img src="https://github.com/ptrstn/shellsmith/actions/workflows/test.yaml/badge.svg" alt="Test"></a>
65
+ <a href="https://codecov.io/gh/ptrstn/shellsmith"><img src="https://codecov.io/gh/ptrstn/shellsmith/branch/main/graph/badge.svg" alt="codecov"></a>
66
+ <a href="https://pypi.org/project/shellsmith"><img src="https://img.shields.io/pypi/v/shellsmith?color=%2334D058" alt="PyPI - Version"></a>
67
+ <a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff"></a>
68
+ </div>
69
+
70
+ <p align="center">
71
+ <b>Documentation</b>: <a href="https://shellsmith.pages.dev/" target="_blank">https://shellsmith.pages.dev</a>
72
+ </p>
73
+
74
+ **Shellsmith** is a Python SDK and CLI for managing [Asset Administration Shells (AAS)](https://industrialdigitaltwin.org/en/content-hub/aasspecifications), Submodels, and Submodel Elements via the [Eclipse BaSyx](https://www.eclipse.org/basyx/) REST API.
75
+
76
+ It provides full client-side access to AAS resources with a clean Python interface and a powerful `typer`-based CLI — ideal for scripting, automation, and digital twin integration workflows.
77
+
78
+ ### Features
79
+
80
+ - 🐍 **Python SDK** for full CRUD access to Shells, Submodels, and Submodel Elements
81
+ - ⚡ **CLI tool** powered by [Typer](https://typer.tiangolo.com/) for fast scripting and automation
82
+ - ⚙️ Simple `.env`-based configuration for flexible environment switching
83
+ - 🔁 Seamless integration with the [Eclipse BaSyx](https://www.eclipse.org/basyx/) Environment REST API
84
+
85
+ ## 🚀 Installation
86
+
87
+ ```bash
88
+ pip install shellsmith
89
+ ```
90
+
91
+ **Requires**: Python 3.10+
92
+
93
+ ## 🔧 Configuration
94
+
95
+ The default AAS environment host is:
96
+
97
+ ```
98
+ http://localhost:8081
99
+ ```
100
+
101
+ You can override it by setting the `SHELLSMITH_BASYX_ENV_HOST` environment variable, or by creating a `.env` file in the current working directory or your project root.
102
+
103
+ ```bash
104
+ SHELLSMITH_BASYX_ENV_HOST=http://your-host:1234
105
+ ```
106
+
107
+ ## 🧠 CLI Usage
108
+
109
+ Shellsmith provides a powerful command-line interface:
110
+
111
+ ```bash
112
+ aas --help
113
+ ```
114
+
115
+ | Command | Description |
116
+ |----------|----------------------------------------------------------|
117
+ | `info` | Display the current Shell tree and identify issues. |
118
+ | `upload` | Upload a single AAS file or all AAS files from a folder. |
119
+ | `nuke` | ☢️ Delete all Shells and Submodels (irrevocable). |
120
+ | `encode` | Encode a value (e.g. Shell ID) to Base64. |
121
+ | `decode` | Decode a Base64-encoded value. |
122
+ | `get` | Get Shells, Submodels, and Submodel Elements. |
123
+ | `delete` | Delete Shells, Submodels, or Submodel Elements. |
124
+ | `update` | Update Shells, Submodels, or Submodel Elements. |
125
+ | `create` | Create new Shells, Submodels, or Submodel Elements. |
126
+
127
+ > ℹ️ Run `aas <command> --help` to view subcommands and options.
128
+
129
+ ### 🔎 Get Commands
130
+
131
+ | Command | Description |
132
+ |---------------------------|---------------------------------------------|
133
+ | `aas get shells` | 🔹 Get all available Shells. |
134
+ | `aas get shell` | 🔹 Get a specific Shell by ID. |
135
+ | `aas get submodel-refs` | 🔹 Get all Submodel References of a Shell. |
136
+ | `aas get submodels` | 🔸 Get all Submodels. |
137
+ | `aas get submodel ` | 🔸 Get a specific Submodel by ID. |
138
+ | `aas get submodel-value ` | 🔸 Get the `$value` of a Submodel. |
139
+ | `aas get submodel-meta` | 🔸 Get the `$metadata` of a Submodel. |
140
+ | `aas get elements` | 🔻 Get all Submodel Elements of a Submodel. |
141
+ | `aas get element` | 🔻 Get a specific Submodel Element. |
142
+ | `aas get element-value` | 🔻 Get the `$value` of a Submodel Element. |
143
+
144
+ ### 🛠️ Create Commands
145
+
146
+ | Command | Description |
147
+ |----------------------------|-----------------------------------------|
148
+ | `aas create shell ` | 🔹 Create a new Shell. |
149
+ | `aas create submodel-ref ` | 🔹 Add a Submodel Reference to a Shell. |
150
+ | `aas create submodel` | 🔸 Create a new Submodel. |
151
+ | `aas create element ` | 🔻 Create a new Submodel Element. |
152
+ | `aas create element` | 🔻 Create an Element at a nested path. |
153
+
154
+ > ℹ️ Input can be passed via `--data "<json>"` or `--file <*.json|*.yaml>`, but **not both**
155
+
156
+ ### 🧬 Update Commands
157
+
158
+ | Command | Description |
159
+ |------------------------------|----------------------------------------------------------------|
160
+ | `aas update shell ` | 🔹 Update a Shell (full replacement). |
161
+ | `aas update submodel` | 🔸 Update a Submodel (full replacement). |
162
+ | `aas update submodel-value ` | 🔸 Update the `$value` of a Submodel (partial update). |
163
+ | `aas update element ` | 🔻 Update a Submodel Element (full replacement). |
164
+ | `aas update element-value ` | 🔻 Update the `$value` of a Submodel Element (partial update). |
165
+
166
+ > ℹ️ All updates are either full replacements (`PUT`) or partial updates (`PATCH`)
167
+
168
+ ### 🧹 Delete Commands
169
+
170
+ | Command | Description |
171
+ |---------------------------|----------------------------------------------------------------|
172
+ | `aas delete shell` | 🔹 Delete a Shell and optionally all referenced Submodels. |
173
+ | `aas delete submodel-ref` | 🔹 Remove a Submodel reference from a Shell. |
174
+ | `aas delete submodel` | 🔸 Delete a Submodel and optionally unlink it from all Shells. |
175
+ | `aas delete element` | 🔻 Delete a Submodel Element. |
176
+
177
+ > ℹ️ You can pass `--cascade` to also remove the Submodels the Shell references
178
+
179
+ > ℹ️ You can pass `--remove-refs` to also remove all references to that Submodel
180
+
181
+ ## 🐍 Python API Usage
182
+
183
+ You can also use `shellsmith` as a Python client library to interact with the BaSyx Environment REST API.
184
+
185
+ ```python
186
+ import shellsmith
187
+
188
+ # List all AAS Shells
189
+ shells = shellsmith.get_shells()
190
+
191
+ # Fetch a specific Shell by ID
192
+ shell = shellsmith.get_shell("https://example.com/shells/my-shell")
193
+
194
+ # List Submodels or Submodel References of a Shell
195
+ submodels = shellsmith.get_submodels()
196
+ refs = shellsmith.get_submodel_refs("https://example.com/shells/my-shell")
197
+
198
+ # Fetch a specific Submodel
199
+ submodel = shellsmith.get_submodel("https://example.com/submodels/my-submodel")
200
+
201
+ # Read and update a Submodel Element's value
202
+ value = shellsmith.get_submodel_element_value(submodel["id"], "temperature")
203
+ shellsmith.patch_submodel_element_value(submodel["id"], "temperature", "42.0")
204
+
205
+ # Upload a single AAS file or an entire folder (.aasx / .json / .xml)
206
+ shellsmith.upload_aas("MyAsset.aasx")
207
+ shellsmith.upload_aas_folder("aas_folder/")
208
+
209
+ # Delete a Shell or Submodel by ID
210
+ shellsmith.delete_shell("https://example.com/aas/my-asset")
211
+ shellsmith.delete_submodel("https://example.com/submodels/my-submodel")
212
+ ```
213
+
214
+ > ℹ️ `shell_id` and `submodel_id` are automatically base64-encoded unless you set `encode=False`. This is required by the BaSyx API for identifier-based URLs.
215
+
216
+ The tables below show the mapping between BaSyx AAS REST API endpoints and the implemented client functions.
217
+
218
+ > 📚 See [Plattform_i40 API reference](https://app.swaggerhub.com/apis/Plattform_i40/Entire-API-Collection) for endpoint details.
219
+
220
+ ### Shells
221
+
222
+ | Method | BaSyx Endpoint | `shellsmith` Function |
223
+ |--------|--------------------------------------------------------------|-----------------------|
224
+ | GET | `/shells` | `get_shells` |
225
+ | POST | `/shells` | `post_shell` |
226
+ | GET | `/shells/{aasIdentifier}` | `get_shell` |
227
+ | PUT | `/shells/{aasIdentifier}` | `put_shell` |
228
+ | DELETE | `/shells/{aasIdentifier}` | `delete_shell` |
229
+ | GET | `/shells/{aasIdentifier}/submodel-refs` | `get_submodel_refs` |
230
+ | POST | `/shells/{aasIdentifier}/submodel-refs` | `post_submodel_ref` |
231
+ | DELETE | `/shells/{aasIdentifier}/submodel-refs/{submodelIdentifier}` | `delete_submodel_ref` |
232
+
233
+ ### Submodels
234
+
235
+ | Method | BaSyx Endpoint | Shellsmith Function |
236
+ |--------|---------------------------------------------|-------------------------|
237
+ | GET | `/submodels` | `get_submodels` |
238
+ | POST | `/submodels` | `post_submodel` |
239
+ | GET | `/submodels/{submodelIdentifier}` | `get_submodel` |
240
+ | PUT | `/submodels/{submodelIdentifier}` | `put_submodel` |
241
+ | DELETE | `/submodels/{submodelIdentifier}` | `delete_submodel` |
242
+ | GET | `/submodels/{submodelIdentifier}/$value` | `get_submodel_value` |
243
+ | PATCH | `/submodels/{submodelIdentifier}/$value` | `patch_submodel_value` |
244
+ | GET | `/submodels/{submodelIdentifier}/$metadata` | `get_submodel_metadata` |
245
+
246
+ ### Submodel Elements
247
+
248
+ | Method | BaSyx Endpoint | Shellsmith Function |
249
+ |--------|--------------------------------------------------------------------------|--------------------------------|
250
+ | GET | `/submodels/{submodelIdentifier}/submodel-elements` | `get_submodel_elements` |
251
+ | POST | `/submodels/{submodelIdentifier}/submodel-elements` | `post_submodel_element` |
252
+ | GET | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `get_submodel_element` |
253
+ | PUT | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `put_submodel_element` |
254
+ | POST | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `post_submodel_element` |
255
+ | DELETE | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `delete_submodel_element` |
256
+ | GET | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/$value` | `get_submodel_element_value` |
257
+ | PATCH | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/$value` | `patch_submodel_element_value` |
258
+
259
+ ### Upload
260
+
261
+ | Method | BaSyx Endpoint | Shellsmith Function |
262
+ |--------|----------------|-----------------------------------------------------|
263
+ | POST | `/upload` | `upload.upload_aas` <br> `upload.upload_aas_folder` |
264
+
265
+ > ℹ️ Upload functions are available under the `shellsmith.upload` submodule.
266
+
267
+ ## ⚙️ Development
268
+
269
+ Clone the repository and set up the virtual environment:
270
+
271
+ ```bash
272
+ git clone https://github.com/ptrstn/shellsmith
273
+ cd shellsmith
274
+ python -m venv .venv
275
+ source .venv/bin/activate # or .venv\Scripts\activate on Windows
276
+ pip install -e .[test]
277
+ ```
278
+
279
+ ### ✅ Testing
280
+
281
+ Start the BaSyx stack (if needed):
282
+
283
+ ```bash
284
+ docker compose up -d
285
+ ```
286
+
287
+ Run the test suite with coverage:
288
+
289
+ ```bash
290
+ pytest --cov
291
+ ```
292
+
293
+ Generate an HTML coverage report:
294
+
295
+ ```bash
296
+ pytest --cov --cov-report=html
297
+ ```
298
+
299
+ Then open `htmlcov/index.html` in your web browser to explore which lines are covered and which are missing.
300
+
301
+ ### 🧼 Code Quality
302
+
303
+ We use [Ruff](https://docs.astral.sh/ruff/) for linting, formatting, and import sorting.
304
+
305
+ Check code style:
306
+
307
+ ```bash
308
+ ruff check
309
+ ```
310
+
311
+ Auto-fix issues:
312
+
313
+ ```bash
314
+ ruff check --fix
315
+ ```
316
+
317
+ Format code:
318
+
319
+ ```bash
320
+ ruff format
321
+ ```
322
+
323
+ ### 📚 Documentation
324
+
325
+ Serve the docs locally:
326
+
327
+ ```bash
328
+ mkdocs serve
329
+ ```
330
+
331
+ Then visit [http://127.0.0.1:8000](http://127.0.0.1:8000) to preview.
332
+
333
+ Build the static site:
334
+
335
+ ```bash
336
+ mkdocs build
337
+ ```
338
+
339
+ ## Resources
340
+
341
+ - https://github.com/eclipse-basyx/basyx-java-server-sdk
342
+ - https://github.com/admin-shell-io/aas-specs-api
343
+ - https://app.swaggerhub.com/apis/Plattform_i40/Entire-API-Collection
@@ -0,0 +1,285 @@
1
+ <div align="center">
2
+ <img src="docs/images/logo-purple.png" alt="shellsmith" style="max-width: 100%; width: 600px;">
3
+ </div>
4
+
5
+ <div align="center">
6
+ <a href="https://github.com/ptrstn/shellsmith/actions/workflows/test.yaml"><img src="https://github.com/ptrstn/shellsmith/actions/workflows/test.yaml/badge.svg" alt="Test"></a>
7
+ <a href="https://codecov.io/gh/ptrstn/shellsmith"><img src="https://codecov.io/gh/ptrstn/shellsmith/branch/main/graph/badge.svg" alt="codecov"></a>
8
+ <a href="https://pypi.org/project/shellsmith"><img src="https://img.shields.io/pypi/v/shellsmith?color=%2334D058" alt="PyPI - Version"></a>
9
+ <a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff"></a>
10
+ </div>
11
+
12
+ <p align="center">
13
+ <b>Documentation</b>: <a href="https://shellsmith.pages.dev/" target="_blank">https://shellsmith.pages.dev</a>
14
+ </p>
15
+
16
+ **Shellsmith** is a Python SDK and CLI for managing [Asset Administration Shells (AAS)](https://industrialdigitaltwin.org/en/content-hub/aasspecifications), Submodels, and Submodel Elements via the [Eclipse BaSyx](https://www.eclipse.org/basyx/) REST API.
17
+
18
+ It provides full client-side access to AAS resources with a clean Python interface and a powerful `typer`-based CLI — ideal for scripting, automation, and digital twin integration workflows.
19
+
20
+ ### Features
21
+
22
+ - 🐍 **Python SDK** for full CRUD access to Shells, Submodels, and Submodel Elements
23
+ - ⚡ **CLI tool** powered by [Typer](https://typer.tiangolo.com/) for fast scripting and automation
24
+ - ⚙️ Simple `.env`-based configuration for flexible environment switching
25
+ - 🔁 Seamless integration with the [Eclipse BaSyx](https://www.eclipse.org/basyx/) Environment REST API
26
+
27
+ ## 🚀 Installation
28
+
29
+ ```bash
30
+ pip install shellsmith
31
+ ```
32
+
33
+ **Requires**: Python 3.10+
34
+
35
+ ## 🔧 Configuration
36
+
37
+ The default AAS environment host is:
38
+
39
+ ```
40
+ http://localhost:8081
41
+ ```
42
+
43
+ You can override it by setting the `SHELLSMITH_BASYX_ENV_HOST` environment variable, or by creating a `.env` file in the current working directory or your project root.
44
+
45
+ ```bash
46
+ SHELLSMITH_BASYX_ENV_HOST=http://your-host:1234
47
+ ```
48
+
49
+ ## 🧠 CLI Usage
50
+
51
+ Shellsmith provides a powerful command-line interface:
52
+
53
+ ```bash
54
+ aas --help
55
+ ```
56
+
57
+ | Command | Description |
58
+ |----------|----------------------------------------------------------|
59
+ | `info` | Display the current Shell tree and identify issues. |
60
+ | `upload` | Upload a single AAS file or all AAS files from a folder. |
61
+ | `nuke` | ☢️ Delete all Shells and Submodels (irrevocable). |
62
+ | `encode` | Encode a value (e.g. Shell ID) to Base64. |
63
+ | `decode` | Decode a Base64-encoded value. |
64
+ | `get` | Get Shells, Submodels, and Submodel Elements. |
65
+ | `delete` | Delete Shells, Submodels, or Submodel Elements. |
66
+ | `update` | Update Shells, Submodels, or Submodel Elements. |
67
+ | `create` | Create new Shells, Submodels, or Submodel Elements. |
68
+
69
+ > ℹ️ Run `aas <command> --help` to view subcommands and options.
70
+
71
+ ### 🔎 Get Commands
72
+
73
+ | Command | Description |
74
+ |---------------------------|---------------------------------------------|
75
+ | `aas get shells` | 🔹 Get all available Shells. |
76
+ | `aas get shell` | 🔹 Get a specific Shell by ID. |
77
+ | `aas get submodel-refs` | 🔹 Get all Submodel References of a Shell. |
78
+ | `aas get submodels` | 🔸 Get all Submodels. |
79
+ | `aas get submodel ` | 🔸 Get a specific Submodel by ID. |
80
+ | `aas get submodel-value ` | 🔸 Get the `$value` of a Submodel. |
81
+ | `aas get submodel-meta` | 🔸 Get the `$metadata` of a Submodel. |
82
+ | `aas get elements` | 🔻 Get all Submodel Elements of a Submodel. |
83
+ | `aas get element` | 🔻 Get a specific Submodel Element. |
84
+ | `aas get element-value` | 🔻 Get the `$value` of a Submodel Element. |
85
+
86
+ ### 🛠️ Create Commands
87
+
88
+ | Command | Description |
89
+ |----------------------------|-----------------------------------------|
90
+ | `aas create shell ` | 🔹 Create a new Shell. |
91
+ | `aas create submodel-ref ` | 🔹 Add a Submodel Reference to a Shell. |
92
+ | `aas create submodel` | 🔸 Create a new Submodel. |
93
+ | `aas create element ` | 🔻 Create a new Submodel Element. |
94
+ | `aas create element` | 🔻 Create an Element at a nested path. |
95
+
96
+ > ℹ️ Input can be passed via `--data "<json>"` or `--file <*.json|*.yaml>`, but **not both**
97
+
98
+ ### 🧬 Update Commands
99
+
100
+ | Command | Description |
101
+ |------------------------------|----------------------------------------------------------------|
102
+ | `aas update shell ` | 🔹 Update a Shell (full replacement). |
103
+ | `aas update submodel` | 🔸 Update a Submodel (full replacement). |
104
+ | `aas update submodel-value ` | 🔸 Update the `$value` of a Submodel (partial update). |
105
+ | `aas update element ` | 🔻 Update a Submodel Element (full replacement). |
106
+ | `aas update element-value ` | 🔻 Update the `$value` of a Submodel Element (partial update). |
107
+
108
+ > ℹ️ All updates are either full replacements (`PUT`) or partial updates (`PATCH`)
109
+
110
+ ### 🧹 Delete Commands
111
+
112
+ | Command | Description |
113
+ |---------------------------|----------------------------------------------------------------|
114
+ | `aas delete shell` | 🔹 Delete a Shell and optionally all referenced Submodels. |
115
+ | `aas delete submodel-ref` | 🔹 Remove a Submodel reference from a Shell. |
116
+ | `aas delete submodel` | 🔸 Delete a Submodel and optionally unlink it from all Shells. |
117
+ | `aas delete element` | 🔻 Delete a Submodel Element. |
118
+
119
+ > ℹ️ You can pass `--cascade` to also remove the Submodels the Shell references
120
+
121
+ > ℹ️ You can pass `--remove-refs` to also remove all references to that Submodel
122
+
123
+ ## 🐍 Python API Usage
124
+
125
+ You can also use `shellsmith` as a Python client library to interact with the BaSyx Environment REST API.
126
+
127
+ ```python
128
+ import shellsmith
129
+
130
+ # List all AAS Shells
131
+ shells = shellsmith.get_shells()
132
+
133
+ # Fetch a specific Shell by ID
134
+ shell = shellsmith.get_shell("https://example.com/shells/my-shell")
135
+
136
+ # List Submodels or Submodel References of a Shell
137
+ submodels = shellsmith.get_submodels()
138
+ refs = shellsmith.get_submodel_refs("https://example.com/shells/my-shell")
139
+
140
+ # Fetch a specific Submodel
141
+ submodel = shellsmith.get_submodel("https://example.com/submodels/my-submodel")
142
+
143
+ # Read and update a Submodel Element's value
144
+ value = shellsmith.get_submodel_element_value(submodel["id"], "temperature")
145
+ shellsmith.patch_submodel_element_value(submodel["id"], "temperature", "42.0")
146
+
147
+ # Upload a single AAS file or an entire folder (.aasx / .json / .xml)
148
+ shellsmith.upload_aas("MyAsset.aasx")
149
+ shellsmith.upload_aas_folder("aas_folder/")
150
+
151
+ # Delete a Shell or Submodel by ID
152
+ shellsmith.delete_shell("https://example.com/aas/my-asset")
153
+ shellsmith.delete_submodel("https://example.com/submodels/my-submodel")
154
+ ```
155
+
156
+ > ℹ️ `shell_id` and `submodel_id` are automatically base64-encoded unless you set `encode=False`. This is required by the BaSyx API for identifier-based URLs.
157
+
158
+ The tables below show the mapping between BaSyx AAS REST API endpoints and the implemented client functions.
159
+
160
+ > 📚 See [Plattform_i40 API reference](https://app.swaggerhub.com/apis/Plattform_i40/Entire-API-Collection) for endpoint details.
161
+
162
+ ### Shells
163
+
164
+ | Method | BaSyx Endpoint | `shellsmith` Function |
165
+ |--------|--------------------------------------------------------------|-----------------------|
166
+ | GET | `/shells` | `get_shells` |
167
+ | POST | `/shells` | `post_shell` |
168
+ | GET | `/shells/{aasIdentifier}` | `get_shell` |
169
+ | PUT | `/shells/{aasIdentifier}` | `put_shell` |
170
+ | DELETE | `/shells/{aasIdentifier}` | `delete_shell` |
171
+ | GET | `/shells/{aasIdentifier}/submodel-refs` | `get_submodel_refs` |
172
+ | POST | `/shells/{aasIdentifier}/submodel-refs` | `post_submodel_ref` |
173
+ | DELETE | `/shells/{aasIdentifier}/submodel-refs/{submodelIdentifier}` | `delete_submodel_ref` |
174
+
175
+ ### Submodels
176
+
177
+ | Method | BaSyx Endpoint | Shellsmith Function |
178
+ |--------|---------------------------------------------|-------------------------|
179
+ | GET | `/submodels` | `get_submodels` |
180
+ | POST | `/submodels` | `post_submodel` |
181
+ | GET | `/submodels/{submodelIdentifier}` | `get_submodel` |
182
+ | PUT | `/submodels/{submodelIdentifier}` | `put_submodel` |
183
+ | DELETE | `/submodels/{submodelIdentifier}` | `delete_submodel` |
184
+ | GET | `/submodels/{submodelIdentifier}/$value` | `get_submodel_value` |
185
+ | PATCH | `/submodels/{submodelIdentifier}/$value` | `patch_submodel_value` |
186
+ | GET | `/submodels/{submodelIdentifier}/$metadata` | `get_submodel_metadata` |
187
+
188
+ ### Submodel Elements
189
+
190
+ | Method | BaSyx Endpoint | Shellsmith Function |
191
+ |--------|--------------------------------------------------------------------------|--------------------------------|
192
+ | GET | `/submodels/{submodelIdentifier}/submodel-elements` | `get_submodel_elements` |
193
+ | POST | `/submodels/{submodelIdentifier}/submodel-elements` | `post_submodel_element` |
194
+ | GET | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `get_submodel_element` |
195
+ | PUT | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `put_submodel_element` |
196
+ | POST | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `post_submodel_element` |
197
+ | DELETE | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `delete_submodel_element` |
198
+ | GET | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/$value` | `get_submodel_element_value` |
199
+ | PATCH | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/$value` | `patch_submodel_element_value` |
200
+
201
+ ### Upload
202
+
203
+ | Method | BaSyx Endpoint | Shellsmith Function |
204
+ |--------|----------------|-----------------------------------------------------|
205
+ | POST | `/upload` | `upload.upload_aas` <br> `upload.upload_aas_folder` |
206
+
207
+ > ℹ️ Upload functions are available under the `shellsmith.upload` submodule.
208
+
209
+ ## ⚙️ Development
210
+
211
+ Clone the repository and set up the virtual environment:
212
+
213
+ ```bash
214
+ git clone https://github.com/ptrstn/shellsmith
215
+ cd shellsmith
216
+ python -m venv .venv
217
+ source .venv/bin/activate # or .venv\Scripts\activate on Windows
218
+ pip install -e .[test]
219
+ ```
220
+
221
+ ### ✅ Testing
222
+
223
+ Start the BaSyx stack (if needed):
224
+
225
+ ```bash
226
+ docker compose up -d
227
+ ```
228
+
229
+ Run the test suite with coverage:
230
+
231
+ ```bash
232
+ pytest --cov
233
+ ```
234
+
235
+ Generate an HTML coverage report:
236
+
237
+ ```bash
238
+ pytest --cov --cov-report=html
239
+ ```
240
+
241
+ Then open `htmlcov/index.html` in your web browser to explore which lines are covered and which are missing.
242
+
243
+ ### 🧼 Code Quality
244
+
245
+ We use [Ruff](https://docs.astral.sh/ruff/) for linting, formatting, and import sorting.
246
+
247
+ Check code style:
248
+
249
+ ```bash
250
+ ruff check
251
+ ```
252
+
253
+ Auto-fix issues:
254
+
255
+ ```bash
256
+ ruff check --fix
257
+ ```
258
+
259
+ Format code:
260
+
261
+ ```bash
262
+ ruff format
263
+ ```
264
+
265
+ ### 📚 Documentation
266
+
267
+ Serve the docs locally:
268
+
269
+ ```bash
270
+ mkdocs serve
271
+ ```
272
+
273
+ Then visit [http://127.0.0.1:8000](http://127.0.0.1:8000) to preview.
274
+
275
+ Build the static site:
276
+
277
+ ```bash
278
+ mkdocs build
279
+ ```
280
+
281
+ ## Resources
282
+
283
+ - https://github.com/eclipse-basyx/basyx-java-server-sdk
284
+ - https://github.com/admin-shell-io/aas-specs-api
285
+ - https://app.swaggerhub.com/apis/Plattform_i40/Entire-API-Collection