sima-cli 0.0.27__py3-none-any.whl → 0.0.29__py3-none-any.whl
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.
- sima_cli/__version__.py +1 -1
- sima_cli/install/metadata_installer.py +26 -0
- sima_cli/serial/serial.py +85 -10
- {sima_cli-0.0.27.dist-info → sima_cli-0.0.29.dist-info}/METADATA +1 -1
- {sima_cli-0.0.27.dist-info → sima_cli-0.0.29.dist-info}/RECORD +9 -9
- {sima_cli-0.0.27.dist-info → sima_cli-0.0.29.dist-info}/WHEEL +0 -0
- {sima_cli-0.0.27.dist-info → sima_cli-0.0.29.dist-info}/entry_points.txt +0 -0
- {sima_cli-0.0.27.dist-info → sima_cli-0.0.29.dist-info}/licenses/LICENSE +0 -0
- {sima_cli-0.0.27.dist-info → sima_cli-0.0.29.dist-info}/top_level.txt +0 -0
sima_cli/__version__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
# sima_cli/__version__.py
|
2
|
-
__version__ = "0.0.
|
2
|
+
__version__ = "0.0.29"
|
@@ -9,6 +9,7 @@ import tarfile
|
|
9
9
|
import zipfile
|
10
10
|
import stat
|
11
11
|
from urllib.parse import urlparse
|
12
|
+
from InquirerPy import inquirer
|
12
13
|
|
13
14
|
from typing import Dict
|
14
15
|
from tqdm import tqdm
|
@@ -104,6 +105,29 @@ def _download_assets(metadata: dict, base_url: str, dest_folder: str, internal:
|
|
104
105
|
|
105
106
|
return local_paths
|
106
107
|
|
108
|
+
def selectable_resource_handler(metadata):
|
109
|
+
selectable = metadata.get("selectable-resources")
|
110
|
+
if not selectable:
|
111
|
+
return metadata
|
112
|
+
|
113
|
+
choices = [(f"{i.get('name','Unnamed')} ({i.get('url','')})" if i.get('url') else i.get('name','Unnamed')) for i in selectable]
|
114
|
+
choices.append("Skip")
|
115
|
+
|
116
|
+
sel = inquirer.select(message="Select an opt-in resource to download:", choices=choices).execute()
|
117
|
+
if sel == "Skip":
|
118
|
+
print("✅ No selectable resource chosen.")
|
119
|
+
return metadata
|
120
|
+
|
121
|
+
idx = choices.index(sel)
|
122
|
+
entry = selectable[idx]
|
123
|
+
res = entry.get("resource")
|
124
|
+
if res:
|
125
|
+
metadata.setdefault("resources", [])
|
126
|
+
if res not in metadata["resources"]:
|
127
|
+
metadata["resources"].append(res)
|
128
|
+
print(f"✅ Selected: {entry.get('name','(unnamed)')} → {res}")
|
129
|
+
return metadata
|
130
|
+
|
107
131
|
def _download_and_validate_metadata(metadata_url, internal=False):
|
108
132
|
"""
|
109
133
|
Downloads (if remote), validates, and parses metadata from a given URL or local file path.
|
@@ -141,6 +165,7 @@ def _download_and_validate_metadata(metadata_url, internal=False):
|
|
141
165
|
metadata = json.load(f)
|
142
166
|
validate_metadata(metadata)
|
143
167
|
click.echo("✅ Metadata validated successfully.")
|
168
|
+
metadata = selectable_resource_handler(metadata)
|
144
169
|
return metadata, os.path.dirname(metadata_path)
|
145
170
|
|
146
171
|
# Common validation logic for local file
|
@@ -148,6 +173,7 @@ def _download_and_validate_metadata(metadata_url, internal=False):
|
|
148
173
|
metadata = json.load(f)
|
149
174
|
|
150
175
|
validate_metadata(metadata)
|
176
|
+
metadata = selectable_resource_handler(metadata)
|
151
177
|
click.echo("✅ Metadata validated successfully.")
|
152
178
|
return metadata, os.path.dirname(os.path.abspath(metadata_path))
|
153
179
|
|
sima_cli/serial/serial.py
CHANGED
@@ -3,6 +3,8 @@ import subprocess
|
|
3
3
|
import shutil
|
4
4
|
import click
|
5
5
|
import os
|
6
|
+
import errno
|
7
|
+
import glob
|
6
8
|
from sima_cli.utils.env import is_sima_board
|
7
9
|
|
8
10
|
def connect_serial(ctx, baud):
|
@@ -69,6 +71,37 @@ def _connect_mac(baud):
|
|
69
71
|
except KeyboardInterrupt:
|
70
72
|
click.echo("\n❎ Serial connection interrupted by user.")
|
71
73
|
|
74
|
+
def _is_busy(port: str) -> bool:
|
75
|
+
# Try fuser
|
76
|
+
try:
|
77
|
+
r = subprocess.run(["fuser", "-s", port], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
78
|
+
if r.returncode == 0:
|
79
|
+
return True
|
80
|
+
if r.returncode == 1:
|
81
|
+
return False
|
82
|
+
except FileNotFoundError:
|
83
|
+
pass
|
84
|
+
|
85
|
+
try:
|
86
|
+
r = subprocess.run(["lsof", "-Fn", port], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
87
|
+
return r.returncode == 0
|
88
|
+
except FileNotFoundError:
|
89
|
+
pass
|
90
|
+
return False
|
91
|
+
|
92
|
+
def _has_rw_permission(port: str) -> bool:
|
93
|
+
# Quick check
|
94
|
+
if not os.access(port, os.R_OK | os.W_OK):
|
95
|
+
# Double-check by attempting to open; some perms lie with access()
|
96
|
+
try:
|
97
|
+
fd = os.open(port, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK)
|
98
|
+
os.close(fd)
|
99
|
+
return True
|
100
|
+
except OSError as e:
|
101
|
+
if e.errno in (errno.EACCES, errno.EPERM):
|
102
|
+
return False
|
103
|
+
# Other errors (e.g., EBUSY) are handled elsewhere
|
104
|
+
return True
|
72
105
|
|
73
106
|
def _connect_linux(baud):
|
74
107
|
terminal = "picocom"
|
@@ -118,20 +151,62 @@ def _connect_linux(baud):
|
|
118
151
|
click.echo("❌ No USB serial device found.")
|
119
152
|
raise SystemExit(1)
|
120
153
|
|
121
|
-
|
154
|
+
# Discover ports
|
155
|
+
ports = sorted(glob.glob("/dev/ttyUSB*"))
|
156
|
+
if not ports:
|
157
|
+
click.echo("❌ No USB serial device found.")
|
158
|
+
raise SystemExit(1)
|
159
|
+
|
160
|
+
# Classify ports
|
161
|
+
busy, no_perm, free_ok = [], [], []
|
162
|
+
for p in ports:
|
163
|
+
if _is_busy(p):
|
164
|
+
busy.append(p)
|
165
|
+
elif not _has_rw_permission(p):
|
166
|
+
no_perm.append(p)
|
167
|
+
else:
|
168
|
+
free_ok.append(p)
|
169
|
+
|
170
|
+
if busy:
|
171
|
+
click.echo("⚠ Busy (in use): " + ", ".join(busy))
|
172
|
+
if no_perm:
|
173
|
+
click.echo("⛔ No permission: " + ", ".join(no_perm))
|
174
|
+
click.echo(
|
175
|
+
"\nTo fix permissions on Ubuntu/Debian:\n"
|
176
|
+
" sudo usermod -aG dialout $USER\n"
|
177
|
+
" # then log out and log back in (or reboot)\n"
|
178
|
+
"Temporary (until reboot):\n"
|
179
|
+
" sudo chmod a+rw /dev/ttyUSBX # not recommended long-term\n"
|
180
|
+
)
|
181
|
+
|
182
|
+
if not free_ok:
|
183
|
+
click.echo("❌ No accessible, free USB serial ports available.")
|
184
|
+
raise SystemExit(1)
|
185
|
+
|
186
|
+
# Choose port
|
187
|
+
if len(free_ok) == 1:
|
188
|
+
port = free_ok[0]
|
189
|
+
click.echo(f"✅ Using the only free port: {port}")
|
190
|
+
else:
|
191
|
+
click.echo("🔍 Multiple free ports found:")
|
192
|
+
for i, p in enumerate(free_ok, 1):
|
193
|
+
click.echo(f" {i}. {p}")
|
194
|
+
idx = click.prompt(f"Select a port [1-{len(free_ok)}]", type=int, default=1)
|
195
|
+
if not (1 <= idx <= len(free_ok)):
|
196
|
+
click.echo("❌ Invalid selection.")
|
197
|
+
raise SystemExit(1)
|
198
|
+
port = free_ok[idx - 1]
|
199
|
+
|
200
|
+
# Connect
|
122
201
|
click.echo(f"🔌 Connecting to {port} with {terminal} ({baud} 8N1)...")
|
123
202
|
try:
|
124
203
|
if terminal == "picocom":
|
125
|
-
click.echo("🧷 To exit:
|
126
|
-
subprocess.run(
|
127
|
-
["sudo", terminal, "-b", f"{baud}", "--databits", "8", "--parity", "n", "--stopbits", "1", port]
|
128
|
-
)
|
204
|
+
click.echo("🧷 To exit: Ctrl+A, then Ctrl+X")
|
205
|
+
subprocess.run([terminal, "-b", f"{baud}", "--databits", "8", "--parity", "n", "--stopbits", "1", port])
|
129
206
|
else: # minicom
|
130
|
-
|
131
|
-
click.echo("🧷 To exit:
|
132
|
-
subprocess.run(
|
133
|
-
["sudo", terminal, "-C", config_file, "-D", port]
|
134
|
-
)
|
207
|
+
cfg = os.path.expanduser("~/.minirc.custom")
|
208
|
+
click.echo("🧷 To exit: Ctrl+A, then Q")
|
209
|
+
subprocess.run([terminal, "-C", cfg, "-D", port])
|
135
210
|
except KeyboardInterrupt:
|
136
211
|
click.echo("\n❎ Serial connection interrupted by user.")
|
137
212
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
sima_cli/__init__.py,sha256=Nb2jSg9-CX1XvSc1c21U9qQ3atINxphuNkNfmR-9P3o,332
|
2
2
|
sima_cli/__main__.py,sha256=ehzD6AZ7zGytC2gLSvaJatxeD0jJdaEvNJvwYeGsWOg,69
|
3
|
-
sima_cli/__version__.py,sha256=
|
3
|
+
sima_cli/__version__.py,sha256=2CZTSSWitwWN8SjkJ0aMqI9jsY-Mo2BPRi6u1w3Ympk,49
|
4
4
|
sima_cli/cli.py,sha256=GYmQ7_XObl9VgFwuWWkWDo-_Y_Vn6lM53F7mKiYGubI,17126
|
5
5
|
sima_cli/app_zoo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
6
|
sima_cli/app_zoo/app.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -14,7 +14,7 @@ sima_cli/download/downloader.py,sha256=nCBrr_0WdnKTIyecwKpg1sCdfm_4PSQTRPwEbiezy
|
|
14
14
|
sima_cli/install/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
15
|
sima_cli/install/hostdriver.py,sha256=kAWDLebs60mbWIyTbUxmNrChcKW1uD5r7FtWNSUVUE4,5852
|
16
16
|
sima_cli/install/metadata_info.py,sha256=wmMqwzGfXbuilkqaxRVrFOzOtTOiONkmPCyA2oDAQpA,2168
|
17
|
-
sima_cli/install/metadata_installer.py,sha256=
|
17
|
+
sima_cli/install/metadata_installer.py,sha256=UPXxXL5gH0iotX8WCUgEbySbYeIHE1UwsfpZvACjZQs,18928
|
18
18
|
sima_cli/install/metadata_validator.py,sha256=7954rp9vFRNnqmIMvCVTjq40kUIEbGXzfc8HmQmChe0,5221
|
19
19
|
sima_cli/install/optiview.py,sha256=i5eWVor-9MScEfrQm3Ty9OP4VpSsCgWvNh7AvYdZu7s,3365
|
20
20
|
sima_cli/install/palette.py,sha256=uRznoHa4Mv9ZXHp6AoqknfC3RxpYNKi9Ins756Cyifk,3930
|
@@ -25,7 +25,7 @@ sima_cli/network/network.py,sha256=ToDCQBfX0bUFEWWtfS8srImK5T11MX6R4MBQFM80faY,7
|
|
25
25
|
sima_cli/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
26
|
sima_cli/sdk/syscheck.py,sha256=h9zCULW67y4i2hqiGc-hc1ucBDShA5FAe9NxwBGq-fM,4575
|
27
27
|
sima_cli/serial/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
|
-
sima_cli/serial/serial.py,sha256=
|
28
|
+
sima_cli/serial/serial.py,sha256=c5k0H2BTJGGCfysmBX1Ijp2pUT9serwdmE6_WyLUiQs,8664
|
29
29
|
sima_cli/storage/nvme.py,sha256=cCzYWcyPwcFu5pSMBkovsS4EwovaIMGolhEFStogXMA,4739
|
30
30
|
sima_cli/storage/sdcard.py,sha256=-WULjdV31-n8v5OOqfxR77qBbIK4hJnrD3xWxUVMoGI,6324
|
31
31
|
sima_cli/update/__init__.py,sha256=0P-z-rSaev40IhfJXytK3AFWv2_sdQU4Ry6ei2sEus0,66
|
@@ -44,7 +44,7 @@ sima_cli/utils/disk.py,sha256=66Kr631yhc_ny19up2aijfycWfD35AeLQOJgUsuH2hY,446
|
|
44
44
|
sima_cli/utils/env.py,sha256=IP5HrH0lE7RMSiBeXcEt5GCLMT5p-QQroG-uGzl5XFU,8181
|
45
45
|
sima_cli/utils/net.py,sha256=WVntA4CqipkNrrkA4tBVRadJft_pMcGYh4Re5xk3rqo,971
|
46
46
|
sima_cli/utils/network.py,sha256=UvqxbqbWUczGFyO-t1SybG7Q-x9kjUVRNIn_D6APzy8,1252
|
47
|
-
sima_cli-0.0.
|
47
|
+
sima_cli-0.0.29.dist-info/licenses/LICENSE,sha256=a260OFuV4SsMZ6sQCkoYbtws_4o2deFtbnT9kg7Rfd4,1082
|
48
48
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
49
49
|
tests/test_app_zoo.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
50
50
|
tests/test_auth.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -53,8 +53,8 @@ tests/test_download.py,sha256=t87DwxlHs26_ws9rpcHGwr_OrcRPd3hz6Zmm0vRee2U,4465
|
|
53
53
|
tests/test_firmware.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
54
|
tests/test_model_zoo.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
55
|
tests/test_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
56
|
-
sima_cli-0.0.
|
57
|
-
sima_cli-0.0.
|
58
|
-
sima_cli-0.0.
|
59
|
-
sima_cli-0.0.
|
60
|
-
sima_cli-0.0.
|
56
|
+
sima_cli-0.0.29.dist-info/METADATA,sha256=1MSfBv5taVOiFdadoLN9hElB33o0aKhHRgU-EgnH-e8,3705
|
57
|
+
sima_cli-0.0.29.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
58
|
+
sima_cli-0.0.29.dist-info/entry_points.txt,sha256=xRYrDq1nCs6R8wEdB3c1kKuimxEjWJkHuCzArQPT0Xk,47
|
59
|
+
sima_cli-0.0.29.dist-info/top_level.txt,sha256=FtrbAUdHNohtEPteOblArxQNwoX9_t8qJQd59fagDlc,15
|
60
|
+
sima_cli-0.0.29.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|