sima-cli 0.0.18__py3-none-any.whl → 0.0.20__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.
@@ -4,7 +4,9 @@ import re
4
4
  import time
5
5
  import tempfile
6
6
  import tarfile
7
+ import gzip
7
8
  import subprocess
9
+ import shutil
8
10
  from InquirerPy import inquirer
9
11
  from urllib.parse import urlparse
10
12
  from typing import List
@@ -12,8 +14,17 @@ from sima_cli.utils.env import get_environment_type
12
14
  from sima_cli.download import download_file_from_url
13
15
  from sima_cli.utils.config_loader import load_resource_config
14
16
  from sima_cli.update.remote import push_and_update_remote_board, get_remote_board_info, reboot_remote_board
15
- from sima_cli.update.local import get_local_board_info, push_and_update_local_board
16
17
  from sima_cli.update.query import list_available_firmware_versions
18
+ from sima_cli.utils.env import is_sima_board
19
+
20
+ if is_sima_board():
21
+ from sima_cli.update import local
22
+ get_local_board_info = local.get_local_board_info
23
+ push_and_update_local_board = local.push_and_update_local_board
24
+ else:
25
+ get_local_board_info = None
26
+ push_and_update_local_board = None
27
+
17
28
 
18
29
  def _resolve_firmware_url(version_or_url: str, board: str, internal: bool = False) -> str:
19
30
  """
@@ -56,31 +67,35 @@ def _pick_from_available_versions(board: str, version_or_url: str, internal: boo
56
67
 
57
68
  available_versions = list_available_firmware_versions(board, version_or_url, internal)
58
69
 
59
- if len(available_versions) > 1:
60
- click.echo("Multiple firmware versions found matching your input:")
70
+ try:
71
+ if len(available_versions) > 1:
72
+ click.echo("Multiple firmware versions found matching your input:")
61
73
 
62
- selected_version = inquirer.fuzzy(
63
- message="Select a version:",
64
- choices=available_versions,
65
- max_height="70%", # scrollable
66
- instruction="(Use ↑↓ to navigate, / to search, Enter to select)"
67
- ).execute()
74
+ selected_version = inquirer.fuzzy(
75
+ message="Select a version:",
76
+ choices=available_versions,
77
+ max_height="70%", # scrollable
78
+ instruction="(Use ↑↓ to navigate, / to search, Enter to select)"
79
+ ).execute()
68
80
 
69
- if not selected_version:
70
- click.echo("No selection made. Exiting.", err=True)
71
- raise SystemExit(1)
81
+ if not selected_version:
82
+ click.echo("No selection made. Exiting.", err=True)
83
+ raise SystemExit(1)
72
84
 
73
- return selected_version
85
+ return selected_version
74
86
 
75
- elif len(available_versions) == 1:
76
- return available_versions[0]
87
+ elif len(available_versions) == 1:
88
+ return available_versions[0]
77
89
 
78
- else:
79
- click.echo(
80
- f"No firmware versions found matching keyword '{version_or_url}' for board '{board}'.",
81
- err=True
82
- )
83
- raise SystemExit(1)
90
+ else:
91
+ click.echo(
92
+ f"No firmware versions found matching keyword '{version_or_url}' for board '{board}'.",
93
+ err=True
94
+ )
95
+ raise SystemExit(1)
96
+ except:
97
+ click.echo("Unable to determine available versions")
98
+ exit(0)
84
99
 
85
100
  def _sanitize_url_to_filename(url: str) -> str:
86
101
  """
@@ -98,22 +113,23 @@ def _sanitize_url_to_filename(url: str) -> str:
98
113
  return safe_name
99
114
 
100
115
 
101
- def _extract_required_files(tar_path: str, board: str) -> list:
116
+ def _extract_required_files(tar_path: str, board: str, update_type: str = 'standard') -> list:
102
117
  """
103
118
  Extract required files from a .tar.gz or .tar archive into the same folder
104
119
  and return the full paths to the extracted files (with subfolder if present).
105
- Skips files that already exist.
120
+ Skips files that already exist. If a .wic.gz file is extracted, it will be decompressed,
121
+ unless the decompressed .wic file already exists.
106
122
 
107
123
  Args:
108
124
  tar_path (str): Path to the downloaded or provided firmware archive.
109
125
  board (str): Board type ('davinci' or 'modalix').
126
+ update_type (str): Update type ('standard' or 'bootimg').
110
127
 
111
128
  Returns:
112
129
  list: List of full paths to extracted files.
113
130
  """
114
131
  extract_dir = os.path.dirname(tar_path)
115
132
 
116
- # Define required filenames (not full paths)
117
133
  target_filenames = {
118
134
  "troot-upgrade-simaai-ev.swu",
119
135
  f"simaai-image-palette-upgrade-{board}.swu"
@@ -123,6 +139,23 @@ def _extract_required_files(tar_path: str, board: str) -> list:
123
139
  if env_type == "host" and _os == "linux":
124
140
  target_filenames.add("sima_pcie_host_pkg.sh")
125
141
 
142
+ if update_type == 'bootimg':
143
+ target_filenames = {
144
+ f"simaai-image-palette-{board}.wic.gz",
145
+ f"simaai-image-palette-{board}.wic.bmap"
146
+ }
147
+ elif update_type == 'netboot':
148
+ target_filenames = {
149
+ "Image",
150
+ "netboot.scr.uimg",
151
+ f"{board}-hhhl.dtb",
152
+ f"{board}-som.dtb",
153
+ f"{board}-dvt.dtb",
154
+ f"{board}-hhhl_x16.dtb",
155
+ f"simaai-image-palette-{board}.wic.gz",
156
+ f"simaai-image-palette-{board}.wic.bmap",
157
+ f"simaai-image-palette-{board}.cpio.gz"
158
+ }
126
159
  extracted_paths = []
127
160
 
128
161
  try:
@@ -140,14 +173,29 @@ def _extract_required_files(tar_path: str, board: str) -> list:
140
173
  if os.path.exists(full_dest_path):
141
174
  click.echo(f"⚠️ Skipping existing file: {full_dest_path}")
142
175
  extracted_paths.append(full_dest_path)
143
- continue
176
+ else:
177
+ os.makedirs(os.path.dirname(full_dest_path), exist_ok=True)
178
+ tar.extract(member, path=extract_dir)
179
+ click.echo(f"✅ Extracted: {full_dest_path}")
180
+ extracted_paths.append(full_dest_path)
144
181
 
145
- # Ensure directory structure exists
146
- os.makedirs(os.path.dirname(full_dest_path), exist_ok=True)
182
+ # If it's a .wic.gz file, decompress it now
183
+ if full_dest_path.endswith(".wic.gz"):
184
+ uncompressed_path = full_dest_path[:-3] # remove .gz
147
185
 
148
- tar.extract(member, path=extract_dir)
149
- extracted_paths.append(full_dest_path)
150
- click.echo(f"✅ Extracted: {full_dest_path}")
186
+ if os.path.exists(uncompressed_path):
187
+ click.echo(f"⚠️ Skipping decompression: {uncompressed_path} already exists")
188
+ extracted_paths.append(uncompressed_path)
189
+ continue
190
+
191
+ try:
192
+ with gzip.open(full_dest_path, 'rb') as f_in:
193
+ with open(uncompressed_path, 'wb') as f_out:
194
+ shutil.copyfileobj(f_in, f_out)
195
+ click.echo(f"📦 Decompressed: {uncompressed_path}")
196
+ extracted_paths.append(uncompressed_path)
197
+ except Exception as decomp_err:
198
+ click.echo(f"❌ Failed to decompress {full_dest_path}: {decomp_err}")
151
199
 
152
200
  if not extracted_paths:
153
201
  click.echo("⚠️ No matching files were found or extracted.")
@@ -158,7 +206,8 @@ def _extract_required_files(tar_path: str, board: str) -> list:
158
206
  click.echo(f"❌ Failed to extract files from archive: {e}")
159
207
  return []
160
208
 
161
- def _download_image(version_or_url: str, board: str, internal: bool = False):
209
+
210
+ def _download_image(version_or_url: str, board: str, internal: bool = False, update_type: str = 'standard'):
162
211
  """
163
212
  Download or use a firmware image for the specified board and version or file path.
164
213
 
@@ -176,7 +225,7 @@ def _download_image(version_or_url: str, board: str, internal: bool = False):
176
225
  # Case 1: Local file provided
177
226
  if os.path.exists(version_or_url) and os.path.isfile(version_or_url):
178
227
  click.echo(f"📁 Using local firmware file: {version_or_url}")
179
- return _extract_required_files(version_or_url, board)
228
+ return _extract_required_files(version_or_url, board, update_type)
180
229
 
181
230
  # Case 2: Treat as custom full URL
182
231
  if version_or_url.startswith("http://") or version_or_url.startswith("https://"):
@@ -198,7 +247,7 @@ def _download_image(version_or_url: str, board: str, internal: bool = False):
198
247
  firmware_path = download_file_from_url(image_url, dest_path, internal=internal)
199
248
 
200
249
  click.echo(f"📦 Firmware downloaded to: {firmware_path}")
201
- return _extract_required_files(firmware_path, board)
250
+ return _extract_required_files(firmware_path, board, update_type)
202
251
 
203
252
  except Exception as e:
204
253
  click.echo(f"❌ Host update failed: {e}")
@@ -328,6 +377,27 @@ def _update_remote(extracted_paths: List[str], ip: str, board: str, passwd: str,
328
377
 
329
378
  return script_path
330
379
 
380
+ def download_image(version_or_url: str, board: str, swtype: str, internal: bool = False, update_type: str = 'standard'):
381
+ """
382
+ Download and extract a firmware image for a specified board.
383
+
384
+ Args:
385
+ version_or_url (str): Either a version string (e.g., "1.6.0") or a direct URL or local file path to the image.
386
+ board (str): The board type (e.g., "mlsoc", "modalix").
387
+ swtype (str): The software type (default to 'davinci', possible values: `davinci`, `modalix`): not supported for now
388
+ internal (bool): Whether to use internal download paths (e.g., Artifactory).
389
+ update_type (str): Whether this is standard update or writing boot image.
390
+
391
+ Returns:
392
+ List[str]: Paths to the extracted image files.
393
+ """
394
+
395
+ if 'http' not in version_or_url and not os.path.exists(version_or_url):
396
+ version_or_url = _pick_from_available_versions(board, version_or_url, internal)
397
+
398
+ extracted_paths = _download_image(version_or_url, board, internal, update_type)
399
+ return extracted_paths
400
+
331
401
  def perform_update(version_or_url: str, ip: str = None, internal: bool = False, passwd: str = "edgeai", auto_confirm: bool = False):
332
402
  r"""
333
403
  Update the system based on environment and input.
sima_cli/utils/env.py CHANGED
@@ -36,14 +36,13 @@ def is_pcie_host() -> bool:
36
36
  """
37
37
  Detect if running from a PCIe host (typically a Linux or macOS dev machine).
38
38
 
39
- This assumes a PCIe host is not a SiMa board and is running on a standard
40
- Unix-based OS like Linux or macOS (Darwin).
39
+ This assumes a PCIe host is not a SiMa board and is running on a standard Linux platform.
41
40
 
42
41
  Returns:
43
42
  bool: True if likely a PCIe host, False otherwise.
44
43
  """
45
44
  import platform
46
- return not is_sima_board() and platform.system() in ["Linux", "Darwin"]
45
+ return not is_sima_board() and platform.system() in ["Linux"]
47
46
 
48
47
  def get_sima_board_type() -> str:
49
48
  """
sima_cli/utils/net.py ADDED
@@ -0,0 +1,29 @@
1
+ import psutil
2
+ import socket
3
+
4
+ def get_local_ip_candidates():
5
+ """
6
+ Return a list of IPv4 addresses on physical interfaces,
7
+ excluding VPN, loopback, and link-local interfaces.
8
+ """
9
+ vpn_prefixes = ("tun", "tap", "utun", "tailscale", "wg", "docker") # WireGuard, Tailscale, etc.
10
+ ip_list = []
11
+
12
+ for iface_name, iface_addrs in psutil.net_if_addrs().items():
13
+ # Exclude VPN or tunnel interfaces
14
+ if iface_name.startswith(vpn_prefixes):
15
+ continue
16
+
17
+ for addr in iface_addrs:
18
+ if addr.family == socket.AF_INET:
19
+ ip = addr.address
20
+
21
+ # Skip loopback and link-local
22
+ if ip.startswith("127.") or ip.startswith("169.254."):
23
+ continue
24
+
25
+ ip_list.append((iface_name, ip))
26
+
27
+ # Prioritize physical interfaces: eth0, en0, wlan0, etc.
28
+ ip_list.sort(key=lambda x: (not x[0].startswith(("eth", "en", "wlan", "wl")), x[0]))
29
+ return ip_list
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sima-cli
3
- Version: 0.0.18
3
+ Version: 0.0.20
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
@@ -24,6 +24,8 @@ Requires-Dist: paramiko
24
24
  Requires-Dist: plotext
25
25
  Requires-Dist: rich
26
26
  Requires-Dist: InquirerPy
27
+ Requires-Dist: tftpy
28
+ Requires-Dist: psutil
27
29
  Dynamic: author
28
30
  Dynamic: license-file
29
31
  Dynamic: requires-python
@@ -1,33 +1,43 @@
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=8-9gPM1y6DdoYBg3txOjyT5ldXc4k9e8BvBxlen4c6g,49
4
- sima_cli/cli.py,sha256=EZkY0FaGLc2krp5Rf0AuUT8t3qktoE5h9vIY8-wkHKk,8263
3
+ sima_cli/__version__.py,sha256=U-M6HF-1PsXkez7yhqMRRsr6FObzLVRQ6Spmo4bkl30,49
4
+ sima_cli/cli.py,sha256=b4ScjEa5Ltikry4ftZdJaNxBlSMYtmjYlodqzbRyWRk,12658
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
7
7
  sima_cli/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  sima_cli/auth/basic_auth.py,sha256=pcocI6v496vC7v5MLYPZq3AEgD-2DdkzNFZiKsbx3eQ,4290
9
- sima_cli/auth/login.py,sha256=e9wBEeo_60qplODHBeK84R7zusT3K07kdFoZC1mkSrQ,3778
9
+ sima_cli/auth/login.py,sha256=yCYXWgrfbP4jSTZ3hITfxlgHkdVQVzsd8hQKpqaqCKs,3780
10
10
  sima_cli/data/resources_internal.yaml,sha256=zlQD4cSnZK86bLtTWuvEudZTARKiuIKmB--Jv4ajL8o,200
11
11
  sima_cli/data/resources_public.yaml,sha256=U7hmUomGeQ2ULdo1BU2OQHr0PyKBamIdK9qrutDlX8o,201
12
12
  sima_cli/download/__init__.py,sha256=6y4O2FOCYFR2jdnQoVi3hRtEoZ0Gw6rydlTy1SGJ5FE,218
13
13
  sima_cli/download/downloader.py,sha256=pHfqcg_ujBQjds_EkcRV85M2mRYGrysoZaiR-FIrpf4,5161
14
+ sima_cli/install/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ sima_cli/install/hostdriver.py,sha256=kAWDLebs60mbWIyTbUxmNrChcKW1uD5r7FtWNSUVUE4,5852
16
+ sima_cli/install/optiview.py,sha256=i5eWVor-9MScEfrQm3Ty9OP4VpSsCgWvNh7AvYdZu7s,3365
17
+ sima_cli/install/palette.py,sha256=uRznoHa4Mv9ZXHp6AoqknfC3RxpYNKi9Ins756Cyifk,3930
14
18
  sima_cli/mla/meminfo.py,sha256=ndc8kQJmWGEIdvNh6iIhATGdrkqM2pbddr_eHxaPNfg,1466
15
19
  sima_cli/model_zoo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
20
  sima_cli/model_zoo/model.py,sha256=q91Nrg62j1TqwPO8HiX4nlEFCCmzNEFcyFTBVMbJm8w,9836
17
21
  sima_cli/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
22
  sima_cli/sdk/syscheck.py,sha256=h9zCULW67y4i2hqiGc-hc1ucBDShA5FAe9NxwBGq-fM,4575
23
+ sima_cli/serial/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ sima_cli/serial/serial.py,sha256=6xRta_PzE_DmmooYq35lbK76TYpAny5SEJAdYC_3fH0,4141
19
25
  sima_cli/update/__init__.py,sha256=0P-z-rSaev40IhfJXytK3AFWv2_sdQU4Ry6ei2sEus0,66
26
+ sima_cli/update/bmaptool.py,sha256=KrhUGShBwY4Wzz50QiuMYAxxPgEy1nz5C68G-0a4qF4,4988
27
+ sima_cli/update/bootimg.py,sha256=AOZI9bXrY8x-1gwlbIINdOUNO_naw9qteNJqeQ9_o-Y,13407
20
28
  sima_cli/update/local.py,sha256=CyUFLs5Lz5w4VyM6ip4wndKBBLz3_KZ-3scEvSiOrcg,3299
29
+ sima_cli/update/netboot.py,sha256=xTtRf8LMuqC_Ye-m6tlv5kbwkZwexc623kRymwiLTf4,18528
21
30
  sima_cli/update/query.py,sha256=cVkUMLZkONJ2XMEwqEC-JqLVB38hOqfWM2hB2ehBK6Y,3272
22
- sima_cli/update/remote.py,sha256=wMS39qhXV99fjkeh_0C5dDx1RdE8ooGniEHl_W6v3MA,8877
23
- sima_cli/update/updater.py,sha256=8CxRcIRnCYRx80Xy2zBJ_Ni5dYejASQvgSV6KWXCDTk,15613
31
+ sima_cli/update/remote.py,sha256=RXQbNCDr7d8wLJ7pdGwA6G3gzgwrfZ9l_7YNYUfGHDU,10067
32
+ sima_cli/update/updater.py,sha256=1K87YDj_isuMtL38ZI-hwBTIRmk3WqbkE6s-J0IVp-Q,18884
24
33
  sima_cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
34
  sima_cli/utils/artifactory.py,sha256=6YyVpzVm8ATy7NEwT9nkWx-wptkXrvG7Wl_zDT6jmLs,2390
26
35
  sima_cli/utils/config.py,sha256=wE-cPQqY_gOqaP8t01xsRHD9tBUGk9MgBUm2GYYxI3E,1616
27
36
  sima_cli/utils/config_loader.py,sha256=7I5we1yiCai18j9R9jvhfUzAmT3OjAqVK35XSLuUw8c,2005
28
- sima_cli/utils/env.py,sha256=INOAVfCFwh0BHeqSMmEHXfpIyepNha4-SMI1iOOplOo,5795
37
+ sima_cli/utils/env.py,sha256=Jrb062EnpMBr1jGMotBlI2j9LEH6W1Z5Tgt6LHY7yYQ,5753
38
+ sima_cli/utils/net.py,sha256=WVntA4CqipkNrrkA4tBVRadJft_pMcGYh4Re5xk3rqo,971
29
39
  sima_cli/utils/network.py,sha256=UvqxbqbWUczGFyO-t1SybG7Q-x9kjUVRNIn_D6APzy8,1252
30
- sima_cli-0.0.18.dist-info/licenses/LICENSE,sha256=a260OFuV4SsMZ6sQCkoYbtws_4o2deFtbnT9kg7Rfd4,1082
40
+ sima_cli-0.0.20.dist-info/licenses/LICENSE,sha256=a260OFuV4SsMZ6sQCkoYbtws_4o2deFtbnT9kg7Rfd4,1082
31
41
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
42
  tests/test_app_zoo.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
43
  tests/test_auth.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -36,8 +46,8 @@ tests/test_download.py,sha256=t87DwxlHs26_ws9rpcHGwr_OrcRPd3hz6Zmm0vRee2U,4465
36
46
  tests/test_firmware.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
47
  tests/test_model_zoo.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
48
  tests/test_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
- sima_cli-0.0.18.dist-info/METADATA,sha256=8o5Si6o1A3Nja7M4V3mTfY97TiEY12U7z-mCrQOpf-Q,3631
40
- sima_cli-0.0.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
41
- sima_cli-0.0.18.dist-info/entry_points.txt,sha256=xRYrDq1nCs6R8wEdB3c1kKuimxEjWJkHuCzArQPT0Xk,47
42
- sima_cli-0.0.18.dist-info/top_level.txt,sha256=FtrbAUdHNohtEPteOblArxQNwoX9_t8qJQd59fagDlc,15
43
- sima_cli-0.0.18.dist-info/RECORD,,
49
+ sima_cli-0.0.20.dist-info/METADATA,sha256=0KwVrHOfciP7OQWLcpOhnarYPVPkTcffT4QSKNXqzwo,3674
50
+ sima_cli-0.0.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
51
+ sima_cli-0.0.20.dist-info/entry_points.txt,sha256=xRYrDq1nCs6R8wEdB3c1kKuimxEjWJkHuCzArQPT0Xk,47
52
+ sima_cli-0.0.20.dist-info/top_level.txt,sha256=FtrbAUdHNohtEPteOblArxQNwoX9_t8qJQd59fagDlc,15
53
+ sima_cli-0.0.20.dist-info/RECORD,,