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 CHANGED
@@ -1,2 +1,2 @@
1
1
  # sima_cli/__version__.py
2
- __version__ = "0.0.27"
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
- port = ports[0]
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: Press Ctrl + A, then Ctrl + X")
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
- config_file = os.path.expanduser("~/.minirc.custom")
131
- click.echo("🧷 To exit: Press Ctrl + A, then Q")
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
  Metadata-Version: 2.4
2
2
  Name: sima-cli
3
- Version: 0.0.27
3
+ Version: 0.0.29
4
4
  Summary: CLI tool for SiMa Developer Portal to download models, firmware, and apps.
5
5
  Home-page: https://developer.sima.ai/
6
6
  Author: SiMa.ai
@@ -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=gxBLTfuQW1ckn_VwGnJeXR1eVJD4qo__oTN9DM-b2n8,49
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=itAvglQO4ZAJBGB2nBuyYJdsMb41gN183MaBEDgU2_Y,17935
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=1We85F9-l1FZcsLFxRzxbfPxAHeSCVlBOUyOUpqNf_s,6202
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.27.dist-info/licenses/LICENSE,sha256=a260OFuV4SsMZ6sQCkoYbtws_4o2deFtbnT9kg7Rfd4,1082
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.27.dist-info/METADATA,sha256=lzAzsKQImaK7MEelhD0MDkPGTf2x8dyaRsXR3dj27bA,3705
57
- sima_cli-0.0.27.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
58
- sima_cli-0.0.27.dist-info/entry_points.txt,sha256=xRYrDq1nCs6R8wEdB3c1kKuimxEjWJkHuCzArQPT0Xk,47
59
- sima_cli-0.0.27.dist-info/top_level.txt,sha256=FtrbAUdHNohtEPteOblArxQNwoX9_t8qJQd59fagDlc,15
60
- sima_cli-0.0.27.dist-info/RECORD,,
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,,