sima-cli 0.0.18__py3-none-any.whl → 0.0.19__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.18"
2
+ __version__ = "0.0.19"
sima_cli/auth/login.py CHANGED
@@ -61,14 +61,14 @@ def login_internal():
61
61
  click.echo(f"✅ Identity token is valid")
62
62
 
63
63
  # Step 2: Exchange for a short-lived access token (default: 7 days)
64
- access_token, user_name = exchange_identity_token(identity_token, exchange_url, expires_in=604800)
64
+ access_token, user_name = exchange_identity_token(identity_token, exchange_url, expires_in=1209600)
65
65
 
66
66
  if not access_token:
67
67
  return click.echo("❌ Failed to acquire short-lived access token.")
68
68
 
69
69
  # Step 3: Save token to internal auth config
70
70
  set_auth_token(access_token, internal=True)
71
- click.echo(f"💾 Short-lived access token saved successfully for {user_name} (valid for 7 days).")
71
+ click.echo(f"💾 Short-lived access token saved successfully for {user_name} (valid for 14 days).")
72
72
 
73
73
 
74
74
  def _login_external():
sima_cli/cli.py CHANGED
@@ -7,6 +7,8 @@ from sima_cli.utils.config_loader import internal_resource_exists
7
7
  from sima_cli.mla.meminfo import monitor_simaai_mem_chart
8
8
  from sima_cli.__version__ import __version__
9
9
  from sima_cli.utils.config import CONFIG_PATH
10
+ from sima_cli.install.optiview import install_optiview
11
+ from sima_cli.install.hostdriver import install_hostdriver
10
12
 
11
13
  # Entry point for the CLI tool using Click's command group decorator
12
14
  @click.group()
@@ -164,7 +166,7 @@ def modelzoo(ctx, ver):
164
166
  ctx.obj['ver'] = ver
165
167
  internal = ctx.obj.get("internal", False)
166
168
  if not internal:
167
- click.echo(f"external environment is not supported yet..")
169
+ click.echo(f"Public developer portal environment is not supported yet..")
168
170
  exit(0)
169
171
 
170
172
  pass
@@ -217,6 +219,84 @@ def show_mla_memory_usage(ctx):
217
219
  monitor_simaai_mem_chart()
218
220
  pass
219
221
 
222
+
223
+ # ----------------------
224
+ # bootimg Command
225
+ # ----------------------
226
+ @main.command(name="bootimg")
227
+ @click.option("-v", "--version", required=True, help="Firmware version to download and write (e.g., 1.6.0)")
228
+ @click.option("--boardtype", type=click.Choice(["modalix", "mlsoc"], case_sensitive=False), default="mlsoc", show_default=True, help="Target board type.")
229
+ @click.pass_context
230
+ def bootimg_cmd(ctx, version, boardtype):
231
+ """
232
+ Download and burn a bootable image onto removable media (e.g., SD card or USB stick).
233
+
234
+ Examples:
235
+ sima-cli bootimg -v 1.6.0
236
+ sima-cli bootimg -v 1.6.0 --boardtype mlsoc
237
+ sima-cli bootimg -v 1.6.0 --boardtype modalix
238
+ """
239
+
240
+ from sima_cli.update.bootimg import write_image
241
+
242
+ internal = ctx.obj.get("internal", False)
243
+
244
+ click.echo(f"📦 Preparing boot image:")
245
+ click.echo(f" 🔹 Version : {version}")
246
+ click.echo(f" 🔹 Board Type: {boardtype}")
247
+
248
+ try:
249
+ boardtype = boardtype if boardtype != 'mlsoc' else 'davinci'
250
+ write_image(version, boardtype, 'yocto', internal)
251
+ click.echo("✅ Boot image successfully written.")
252
+ except Exception as e:
253
+ click.echo(f"❌ Failed to write boot image: {e}", err=True)
254
+ ctx.exit(1)
255
+
256
+ # ----------------------
257
+ # install Command
258
+ # ----------------------
259
+ SDK_DEPENDENT_COMPONENTS = {"palette", "hostdriver"}
260
+ SDK_INDEPENDENT_COMPONENTS = {"optiview"}
261
+ ALL_COMPONENTS = SDK_DEPENDENT_COMPONENTS | SDK_INDEPENDENT_COMPONENTS
262
+
263
+ @main.command(name="install")
264
+ @click.argument("component", type=click.Choice(ALL_COMPONENTS, case_sensitive=False))
265
+ @click.option("-v", "--version", help="SDK version (required for SDK-dependent components)")
266
+ @click.pass_context
267
+ def install_cmd(ctx, component, version):
268
+ """
269
+ Install supported components such as SDKs or tools.
270
+
271
+ Examples:
272
+ cli install palette -v 1.6.0
273
+ cli install hostdriver -v 1.6.0
274
+
275
+ cli install optiview
276
+ """
277
+ component = component.lower()
278
+ internal = ctx.obj.get("internal", False)
279
+
280
+ if component in SDK_DEPENDENT_COMPONENTS and not version:
281
+ click.echo(f"❌ The component '{component}' requires a specific SDK version. Please provide one using -v.")
282
+ ctx.exit(1)
283
+
284
+ if component in SDK_INDEPENDENT_COMPONENTS and version:
285
+ click.echo(f"ℹ️ The component '{component}' does not require an SDK version. Ignoring -v {version}.")
286
+
287
+ # Perform the installation logic
288
+ if component == "palette":
289
+ click.echo(f"🔧 Installing SDK component 'palette' for version {version} is not implemented yet...")
290
+ elif component == "hostdriver":
291
+ click.echo(f"🔧 Installing SDK component 'hostdriver' for version {version}...")
292
+ install_hostdriver(version=version, internal=internal)
293
+ elif component == "optiview":
294
+ click.echo("🔧 Installing tool 'optiview'...")
295
+ install_optiview()
296
+
297
+ click.echo("✅ Installation complete.")
298
+
299
+
220
300
  # ----------------------
221
301
  # App Zoo Subcommands
222
302
  # ----------------------
File without changes
@@ -0,0 +1,152 @@
1
+ import sys
2
+ import click
3
+ import os
4
+ import subprocess
5
+ from pathlib import Path
6
+
7
+ from sima_cli.update.updater import download_image
8
+ from sima_cli.utils.env import is_pcie_host
9
+
10
+ def _check_driver_installation():
11
+ """
12
+ Checks whether the PCIe host driver (sima_mla_drv) and related components are correctly installed.
13
+
14
+ Returns:
15
+ dict: A dictionary with validation results for gstreamer, kernel module, and PCI device.
16
+ """
17
+ results = {}
18
+
19
+ # 1. Check GStreamer plugin
20
+ try:
21
+ gst_output = subprocess.check_output(["gst-inspect-1.0", "pciehost"], stderr=subprocess.STDOUT, text=True)
22
+ if "pciehost" in gst_output:
23
+ results["gstreamer"] = {
24
+ "success": True,
25
+ "message": "✅ GStreamer plugin 'pciehost' is installed."
26
+ }
27
+ else:
28
+ results["gstreamer"] = {
29
+ "success": False,
30
+ "message": "❌ GStreamer plugin 'pciehost' not found."
31
+ }
32
+ except subprocess.CalledProcessError as e:
33
+ results["gstreamer"] = {
34
+ "success": False,
35
+ "message": f"❌ GStreamer check failed: {e.output.strip()}"
36
+ }
37
+
38
+ # 2. Check kernel module
39
+ try:
40
+ modinfo_output = subprocess.check_output(["modinfo", "sima_mla_drv"], stderr=subprocess.STDOUT, text=True)
41
+ if "filename:" in modinfo_output and "sima_mla_drv" in modinfo_output:
42
+ results["kernel_module"] = {
43
+ "success": True,
44
+ "message": "✅ Kernel module 'sima_mla_drv' is installed."
45
+ }
46
+ else:
47
+ results["kernel_module"] = {
48
+ "success": False,
49
+ "message": "❌ Kernel module 'sima_mla_drv' not found."
50
+ }
51
+ except subprocess.CalledProcessError as e:
52
+ results["kernel_module"] = {
53
+ "success": False,
54
+ "message": f"❌ Kernel module check failed: {e.output.strip()}"
55
+ }
56
+
57
+ # 3. Check PCI device presence
58
+ try:
59
+ lspci_output = subprocess.check_output(["lspci", "-vd", "1f06:abcd"], stderr=subprocess.STDOUT, text=True)
60
+ if "sima_mla_drv" in lspci_output or "1f06:abcd" in lspci_output:
61
+ results["pci_device"] = {
62
+ "success": True,
63
+ "message": "✅ PCIe device (1f06:abcd) detected and bound to 'sima_mla_drv'."
64
+ }
65
+ else:
66
+ results["pci_device"] = {
67
+ "success": False,
68
+ "message": "❌ PCIe device not detected."
69
+ }
70
+ except subprocess.CalledProcessError as e:
71
+ results["pci_device"] = {
72
+ "success": False,
73
+ "message": f"❌ PCIe device check failed: {e.output.strip()}"
74
+ }
75
+
76
+ return results
77
+
78
+ def _print_driver_validation_table(results: dict):
79
+ """
80
+ Prints the driver validation results in a table format with fixed-width columns.
81
+ """
82
+ print("\nDriver Installation Validation:\n")
83
+ print(f"{'Component':<20} | {'Status':<10} | {'Details'}")
84
+ print("-" * 60)
85
+
86
+ for section, result in results.items():
87
+ component = section.replace("_", " ").title()
88
+ status = "PASS" if result["success"] else "FAIL"
89
+ message = result["message"]
90
+ print(f"{component:<20} | {status:<10} | {message}")
91
+
92
+ def install_hostdriver(version: str, internal: bool = False):
93
+ """
94
+ Install PCIe host driver on supported platforms.
95
+
96
+ This function is only valid on PCIe host machines. It downloads the appropriate image
97
+ package and installs the host driver script if present.
98
+
99
+ Args:
100
+ version (str): Firmware version string (e.g., "1.6.0").
101
+ internal (bool): Whether to use internal sources for the download.
102
+
103
+ Raises:
104
+ RuntimeError: If the platform is not supported or the driver script is missing.
105
+ """
106
+ if not is_pcie_host():
107
+ click.echo("❌ This command is only supported on PCIe host Linux machines.")
108
+ sys.exit(1)
109
+
110
+ try:
111
+ # Step 1: Install system dependencies
112
+ click.echo("🔧 Installing required system packages...")
113
+ try:
114
+ subprocess.run([
115
+ "sudo", "apt-get", "update"
116
+ ], check=True)
117
+ subprocess.run([
118
+ "sudo", "apt-get", "install", "-y",
119
+ "make", "cmake", "gcc", "g++", "dkms", "doxygen",
120
+ "libjson-c-dev", "libjsoncpp-dev", "build-essential", "linux-headers-generic"
121
+ ], check=True)
122
+ click.echo("✅ Dependencies installed successfully.")
123
+ except subprocess.CalledProcessError as e:
124
+ raise RuntimeError(f"❌ Failed to install dependencies: {e}")
125
+
126
+ # Step 2: Download driver package which is part of the firmware release.
127
+ click.echo(f"⬇️ Downloading host driver package for version {version}...")
128
+ extracted_files = download_image(version_or_url=version, board="davinci", swtype="yocto", internal=internal)
129
+
130
+ # Find the host driver script in extracted files
131
+ script_path = next((f for f in extracted_files if f.endswith("sima_pcie_host_pkg.sh")), None)
132
+
133
+ if not script_path or not os.path.isfile(script_path):
134
+ raise RuntimeError("sima_pcie_host_pkg.sh not found in the downloaded package.")
135
+
136
+ click.echo(f"📦 Host driver script found: {script_path}")
137
+ click.confirm("⚠️ This will install drivers on your system using sudo. Continue?", abort=True)
138
+
139
+ # Make sure the script is executable
140
+ os.chmod(script_path, 0o755)
141
+
142
+ # Run the script with sudo
143
+ subprocess.run(["sudo", script_path], check=True)
144
+
145
+ click.echo("✅ Host driver installation completed.")
146
+
147
+ results = _check_driver_installation()
148
+ _print_driver_validation_table(results)
149
+
150
+ except Exception as e:
151
+ raise RuntimeError(f"❌ Failed to install host driver: {e}")
152
+
@@ -0,0 +1,80 @@
1
+ import sys
2
+ import platform
3
+ import click
4
+ import urllib
5
+ import json
6
+ import tempfile
7
+ import tarfile
8
+ import os
9
+ import subprocess
10
+ from pathlib import Path
11
+ from sima_cli.download import download_file_from_url
12
+ from sima_cli.utils.env import is_sima_board
13
+ from sima_cli.utils.config_loader import load_resource_config
14
+
15
+ def install_optiview():
16
+ try:
17
+ # Special path for SiMa DevKit
18
+ if is_sima_board():
19
+ click.echo("🛠 Detected SiMa DevKit. Cleaning up existing installation...")
20
+ subprocess.run(["sudo", "pip3", "uninstall", "-y", "optiview"], check=True)
21
+ click.echo("📦 Installing Optiview via pip...")
22
+ subprocess.run(["sudo", "pip3", "install", "optiview"], check=True)
23
+ click.echo("✅ Optiview installed successfully on DevKit.")
24
+ return
25
+
26
+ cfg = load_resource_config()
27
+ download_url_base = cfg.get('public').get('download').get('download_url')
28
+
29
+ # Normal flow for other platforms
30
+ click.echo("📥 Fetching Optiview version info...")
31
+ version_url = f"{download_url_base}optiview/metadata.json"
32
+ downloads_dir = Path.home() / "Downloads" / "optiview-installer"
33
+ downloads_dir.mkdir(parents=True, exist_ok=True)
34
+ metadata_path = download_file_from_url(version_url, dest_folder=downloads_dir, internal=False)
35
+
36
+ with open(metadata_path, "r") as f:
37
+ version_data = json.load(f)
38
+ latest_version = version_data.get("latest")
39
+
40
+ if not latest_version:
41
+ raise Exception("Unable to retrieve latest version info.")
42
+
43
+ # Determine architecture
44
+ system = platform.system().lower()
45
+ machine = platform.machine().lower()
46
+
47
+ if system == "darwin" and machine == "arm64":
48
+ arch = "aarch64"
49
+ else:
50
+ arch = "x86_64"
51
+
52
+ pkg_url = f"{download_url_base}optiview/optiview-installation-{arch}-{latest_version}.tar.gz"
53
+ click.echo(f"🌐 Downloading Optiview ({arch} v{latest_version})...")
54
+
55
+ archive_path = download_file_from_url(pkg_url, dest_folder=downloads_dir)
56
+
57
+ click.echo(f"📦 Extracting to {downloads_dir}...")
58
+ with tarfile.open(archive_path, "r:gz") as tar:
59
+ tar.extractall(path=downloads_dir)
60
+
61
+ install_script = "install.bat" if system == "windows" else "install.sh"
62
+ script_path = os.path.join(downloads_dir, install_script)
63
+
64
+ if not os.path.isfile(script_path):
65
+ raise Exception(f"Installation script not found: {script_path}")
66
+
67
+ click.echo(f"🚀 Running installer: {install_script}")
68
+ if system == "windows":
69
+ subprocess.run(["cmd", "/c", os.path.basename(script_path)], check=True, cwd=downloads_dir)
70
+ else:
71
+ subprocess.run(["bash", os.path.basename(script_path)], check=True, cwd=downloads_dir)
72
+
73
+ click.echo(f"✅ Optiview installed successfully. Run {downloads_dir}/run.sh to start optiview")
74
+
75
+ except Exception as e:
76
+ click.echo(f"❌ Installation failed: {e}")
77
+ sys.exit(1)
78
+
79
+ if __name__ == "__main__":
80
+ install_optiview()
@@ -0,0 +1,87 @@
1
+ import sys
2
+ import platform
3
+ import click
4
+ import json
5
+ import tarfile
6
+ import os
7
+ import subprocess
8
+ from pathlib import Path
9
+ from sima_cli.download import download_file_from_url
10
+ from sima_cli.utils.env import is_sima_board
11
+ from sima_cli.utils.config_loader import load_resource_config
12
+
13
+ def build_internal_url(download_url_base, latest_filename):
14
+ """Build the URL for internal Palette package."""
15
+ return f"{download_url_base}palette/{latest_filename}"
16
+
17
+ def build_external_url(download_url_base, latest_filename, version):
18
+ """Build the URL for external Palette package from SDKx.y.z folder."""
19
+ return f"{download_url_base}palette/SDK{version}/{latest_filename}"
20
+
21
+ def install_palette(internal=False):
22
+ try:
23
+ # Check for unsupported platforms
24
+ system = platform.system().lower()
25
+ if is_sima_board():
26
+ click.echo("❌ Palette installation is not supported on SiMa DevKit, please install on Ubuntu 22.04 or Windows 10/11.")
27
+ sys.exit(1)
28
+ if system == "darwin":
29
+ click.echo("❌ Palette installation is not supported on macOS, please install on Ubuntu 22.04 or Windows 10/11.")
30
+ sys.exit(1)
31
+
32
+ # Load configuration for resources
33
+ cfg = load_resource_config()
34
+ download_url_base = cfg.get('internal' if internal else 'public').get('download').get('download_url')
35
+
36
+ # Fetch Palette version info
37
+ click.echo(f"📥 Fetching Palette version info ({'internal' if internal else 'external'})...")
38
+ version_url = f"{download_url_base}palette/metadata.json"
39
+ downloads_dir = Path.home() / "Downloads" / "palette-installer"
40
+ downloads_dir.mkdir(parents=True, exist_ok=True)
41
+ metadata_path = download_file_from_url(version_url, dest_folder=downloads_dir, internal=internal)
42
+
43
+ # Read version data
44
+ with open(metadata_path, "r") as f:
45
+ version_data = json.load(f)
46
+ latest_filename = version_data.get("latest")
47
+
48
+ if not latest_filename:
49
+ raise Exception("Unable to retrieve latest filename info.")
50
+
51
+ # Extract version from filename (assuming format like palette-installation-<arch>-x.y.z.tar.gz)
52
+ version = latest_filename.split('-')[-1].replace('.tar.gz', '')
53
+
54
+ # Build package URL based on internal or external
55
+ pkg_url = build_internal_url(download_url_base, latest_filename) if internal else build_external_url(download_url_base, latest_filename, version)
56
+ click.echo(f"🌐 Downloading Palette ({'internal' if internal else 'external'}: {latest_filename})...")
57
+
58
+ # Download the package
59
+ archive_path = download_file_from_url(pkg_url, dest_folder=downloads_dir, internal=internal)
60
+
61
+ # Extract the package
62
+ click.echo(f"📦 Extracting to {downloads_dir}...")
63
+ with tarfile.open(archive_path, "r:gz") as tar:
64
+ tar.extractall(path=downloads_dir)
65
+
66
+ # Determine installation script based on platform
67
+ install_script = "install.bat" if system == "windows" else "install.sh"
68
+ script_path = os.path.join(downloads_dir, install_script)
69
+
70
+ if not os.path.isfile(script_path):
71
+ raise Exception(f"Installation script not found: {script_path}")
72
+
73
+ # Run the installer
74
+ click.echo(f"🚀 Running installer: {install_script}")
75
+ if system == "windows":
76
+ subprocess.run(["cmd", "/c", os.path.basename(script_path)], check=True, cwd=downloads_dir)
77
+ else:
78
+ subprocess.run(["bash", os.path.basename(script_path)], check=True, cwd=downloads_dir)
79
+
80
+ click.echo(f"✅ Palette installed successfully ({'internal' if internal else 'external'}). Run {downloads_dir}/run.sh to start palette")
81
+
82
+ except Exception as e:
83
+ click.echo(f"❌ {'Internal' if internal else 'External'} installation failed: {e}")
84
+ sys.exit(1)
85
+
86
+ if __name__ == "__main__":
87
+ install_palette(internal=False)
@@ -0,0 +1,137 @@
1
+ import subprocess
2
+ import sys
3
+ import os
4
+ import urllib.request
5
+ import tarfile
6
+ import tempfile
7
+ import json
8
+ import shutil
9
+
10
+ def run_command(command, check=True, shell=False, cwd=None):
11
+ """Run a shell command and return its output or raise an exception if it fails."""
12
+ try:
13
+ result = subprocess.run(
14
+ command,
15
+ check=check,
16
+ shell=shell,
17
+ stdout=subprocess.PIPE,
18
+ stderr=subprocess.PIPE,
19
+ text=True,
20
+ cwd=cwd
21
+ )
22
+ return result.stdout.strip(), result.stderr.strip()
23
+ except subprocess.CalledProcessError as e:
24
+ if check:
25
+ print(f"Error running command {command}: {e.stderr}")
26
+ raise
27
+ return "", str(e)
28
+
29
+ def is_bmaptool_installed():
30
+ """Check if bmaptool is installed and callable from the shell."""
31
+ try:
32
+ result = subprocess.run(
33
+ ["bmaptool", "--version"],
34
+ stdout=subprocess.PIPE,
35
+ stderr=subprocess.PIPE,
36
+ text=True,
37
+ timeout=5
38
+ )
39
+ if result.returncode == 0:
40
+ print(f"✅ bmaptool is installed: {result.stdout.strip()}")
41
+ return True
42
+ else:
43
+ print(f"⚠️ bmaptool returned non-zero exit: {result.stderr.strip()}")
44
+ return False
45
+ except (FileNotFoundError, subprocess.TimeoutExpired) as e:
46
+ print(f"❌ bmaptool is not installed or not in PATH: {e}")
47
+ return False
48
+
49
+ def clone_bmaptool_repo(temp_dir, branch_or_tag="main"):
50
+ """Clone the bmaptool repository to a temporary directory."""
51
+ repo_url = "https://github.com/yoctoproject/bmaptool.git"
52
+ print(f"Cloning bmaptool from {repo_url} (branch/tag: {branch_or_tag})...")
53
+ try:
54
+ subprocess.run(
55
+ ["git", "clone", "--depth", "1", "--branch", branch_or_tag, repo_url, temp_dir],
56
+ check=True,
57
+ capture_output=True,
58
+ text=True
59
+ )
60
+ print(f"Cloned bmaptool to {temp_dir}")
61
+ return temp_dir
62
+ except subprocess.CalledProcessError as e:
63
+ print(f"Failed to clone repository: {e.stderr}")
64
+ sys.exit(1)
65
+
66
+ def get_python_and_pip_bins():
67
+ """Get the Python and pip binaries from the active virtual environment or system."""
68
+ if "VIRTUAL_ENV" in os.environ:
69
+ venv_dir = os.environ["VIRTUAL_ENV"]
70
+ print(f"Using existing virtual environment: {venv_dir}")
71
+ if sys.platform == "win32":
72
+ python_bin = os.path.join(venv_dir, "Scripts", "python.exe")
73
+ pip_bin = os.path.join(venv_dir, "Scripts", "pip.exe")
74
+ else:
75
+ python_bin = os.path.join(venv_dir, "bin", "python3")
76
+ pip_bin = os.path.join(venv_dir, "bin", "pip3")
77
+ else:
78
+ print("No virtual environment detected; using system Python.")
79
+ python_bin = shutil.which("python3") or sys.executable
80
+ pip_bin = shutil.which("pip3") or "pip3"
81
+ return python_bin, pip_bin
82
+
83
+ def install_bmaptool(temp_dir):
84
+ """Install bmaptool from the cloned repository."""
85
+ try:
86
+ subprocess.run(
87
+ [sys.executable, "-m", "pip", "install", temp_dir],
88
+ check=True,
89
+ capture_output=True,
90
+ text=True
91
+ )
92
+ print("Installed bmaptool successfully")
93
+ except subprocess.CalledProcessError as e:
94
+ print(f"Failed to install bmaptool: {e.stderr}")
95
+ sys.exit(1)
96
+
97
+ def verify_installation(python_bin):
98
+ """Verify bmaptool installation by running python -m bmaptool --version."""
99
+ stdout, stderr = run_command([python_bin, "-m", "bmaptool", "--version"])
100
+ if stdout:
101
+ print(f"bmaptool installation verified: {stdout}")
102
+ else:
103
+ print(f"Verification failed: {stderr}")
104
+ sys.exit(1)
105
+
106
+ def check_bmap_and_install():
107
+ # Get Python and pip binaries from the active virtual environment or system
108
+ python_bin, _ = get_python_and_pip_bins()
109
+
110
+ # Check if bmaptool is already installed
111
+ if is_bmaptool_installed():
112
+ return
113
+
114
+ # Create a temporary directory
115
+ temp_dir = "bmaptool_temp"
116
+ with tempfile.TemporaryDirectory() as temp_dir:
117
+ if os.path.exists(temp_dir):
118
+ shutil.rmtree(temp_dir)
119
+ os.makedirs(temp_dir)
120
+
121
+ clone_bmaptool_repo(temp_dir, branch_or_tag="main") # Or specify a tag like "v3.8"
122
+ install_bmaptool(temp_dir)
123
+
124
+ # Verify installation
125
+ verify_installation(python_bin)
126
+
127
+ # Provide usage instructions
128
+ if "VIRTUAL_ENV" in os.environ:
129
+ print(f"\nbmaptool is installed in the virtual environment at {os.environ['VIRTUAL_ENV']}")
130
+ print("The virtual environment is already active.")
131
+ print("Run 'bmaptool --help' to see available commands.")
132
+ else:
133
+ print("\nbmaptool is installed in the system Python environment.")
134
+ print("Run 'bmaptool --help' to see available commands.")
135
+
136
+ if __name__ == "__main__":
137
+ check_bmap_and_install()
@@ -0,0 +1,339 @@
1
+ import click
2
+ import platform
3
+ import subprocess
4
+ import sys
5
+ import os
6
+ import select
7
+ import time
8
+ import re
9
+ try:
10
+ from tqdm import tqdm
11
+ except ImportError:
12
+ tqdm = None
13
+ from sima_cli.update.updater import download_image
14
+
15
+
16
+ def list_removable_devices():
17
+ system = platform.system()
18
+
19
+ if system == "Linux":
20
+ return get_linux_removable()
21
+ elif system == "Darwin":
22
+ return get_macos_removable()
23
+ elif system == "Windows":
24
+ return get_windows_removable()
25
+ else:
26
+ click.echo(f"❌ Unsupported platform: {system}")
27
+ return []
28
+
29
+ # Linux: Use lsblk to find removable drives
30
+ def get_linux_removable():
31
+ try:
32
+ output = subprocess.check_output(["lsblk", "-o", "NAME,RM,SIZE,MOUNTPOINT", "-J"]).decode()
33
+ import json
34
+ data = json.loads(output)
35
+ devices = []
36
+ for block in data['blockdevices']:
37
+ if block.get('rm') == True and block.get('mountpoint') is None:
38
+ devices.append({
39
+ "name": block['name'],
40
+ "size": block['size'],
41
+ "path": f"/dev/{block['name']}"
42
+ })
43
+ return devices
44
+ except Exception:
45
+ return []
46
+
47
+ # macOS: Use diskutil
48
+ def get_macos_removable():
49
+ try:
50
+ output = subprocess.check_output(["diskutil", "list"], text=True)
51
+ devices = []
52
+
53
+ candidate_disks = [
54
+ line.split()[0]
55
+ for line in output.splitlines()
56
+ if line.startswith("/dev/disk")
57
+ ]
58
+
59
+ for disk in candidate_disks:
60
+ info = subprocess.check_output(["diskutil", "info", disk], text=True)
61
+ is_removable = False
62
+ is_disk_image = False
63
+ size = "Unknown"
64
+ device_name = ''
65
+
66
+ for info_line in info.splitlines():
67
+ if "Secure Digital" in info_line or "USB" in info_line:
68
+ is_removable = True
69
+ elif "Disk Size" in info_line and "(" in info_line:
70
+ size = info_line.split("(")[1].split()[0]
71
+ elif "Volume Name" in info_line:
72
+ volume_name = info_line.split(":")[-1].strip() or "Unknown"
73
+ elif "Device / Media Name" in info_line:
74
+ is_disk_image = ('Disk Image' in info_line)
75
+ device_name = info_line.split(":")[-1].strip() or "Unknown"
76
+
77
+ # switch to raw device to speed up dd performance
78
+ if is_removable and not is_disk_image:
79
+ devices.append({
80
+ "name": device_name,
81
+ "size": round(int(size) / (1024 ** 3), 0),
82
+ "path": disk.replace('/disk', '/rdisk')
83
+ })
84
+
85
+ return devices
86
+ except Exception as e:
87
+ click.echo(f"Failed to detect removable devices on macOS: {e}")
88
+ return []
89
+
90
+ # Windows: Use wmic or powershell
91
+ def get_windows_removable():
92
+ try:
93
+ output = subprocess.check_output(
94
+ ['powershell', '-Command',
95
+ 'Get-WmiObject Win32_DiskDrive | Where { $_.MediaType -match "Removable" } | '
96
+ 'Select-Object DeviceID,Model,Size | ConvertTo-Json']
97
+ ).decode()
98
+ import json
99
+ parsed = json.loads(output)
100
+ if not isinstance(parsed, list):
101
+ parsed = [parsed]
102
+ devices = []
103
+ for d in parsed:
104
+ size_gb = int(d.get("Size", 0)) // (1024 ** 3)
105
+ devices.append({
106
+ "name": d.get("Model", "Removable Drive"),
107
+ "size": f"{size_gb} GB",
108
+ "path": d["DeviceID"]
109
+ })
110
+ return devices
111
+ except Exception:
112
+ return []
113
+
114
+ def check_dd_installed():
115
+ """Check if dd is installed on the system."""
116
+ try:
117
+ subprocess.run(["which", "dd"], capture_output=True, check=True)
118
+ return True
119
+ except (subprocess.CalledProcessError, FileNotFoundError):
120
+ return False
121
+
122
+ def unmount_device(device_path):
123
+ """Unmount the device using platform-specific commands."""
124
+ system = platform.system()
125
+ try:
126
+ if system == "Darwin": # macOS
127
+ subprocess.run(["diskutil", "unmountDisk", device_path], check=True, capture_output=True, text=True)
128
+ click.echo(f"✅ Unmounted {device_path} on macOS")
129
+ elif system == "Linux":
130
+ result = subprocess.run(["umount", device_path], capture_output=True, text=True)
131
+ if result.returncode == 0:
132
+ click.echo(f"✅ Unmounted {device_path} on Linux")
133
+ elif "not mounted" in result.stderr.lower():
134
+ click.echo(f"ℹ️ {device_path} was not mounted. Continuing.")
135
+ else:
136
+ click.echo(f"❌ Failed to unmount {device_path}: {result.stderr.strip()}")
137
+ sys.exit(1)
138
+ else:
139
+ click.echo(f"❌ Unsupported platform: {system}. Cannot unmount {device_path}.")
140
+ sys.exit(1)
141
+ except Exception as e:
142
+ click.echo(f"❌ Unexpected error while unmounting {device_path}: {e}")
143
+ sys.exit(1)
144
+
145
+ def _require_sudo():
146
+ try:
147
+ # This will prompt for password if necessary and cache it for a few minutes
148
+ click.echo("✅ Running this command requires sudo access.")
149
+ subprocess.run(["sudo", "-v"], check=True)
150
+ except subprocess.CalledProcessError:
151
+ click.echo("❌ Sudo authentication failed.")
152
+ sys.exit(1)
153
+
154
+ def copy_image_to_device(image_path, device_path):
155
+ """Copy the image file to the device using dd with 16M block size."""
156
+ # Get file size for progress calculation
157
+ file_size = os.path.getsize(image_path)
158
+ click.echo(f"ℹ️ Running 'sudo dd' to copy {image_path} to {device_path}")
159
+
160
+ # Debug: Log raw dd output to a file for diagnosis
161
+ debug_log = "dd_output.log"
162
+ click.echo(f"ℹ️ Logging raw dd output to {debug_log} for debugging.")
163
+ _require_sudo()
164
+
165
+ dd_command = ["sudo", "dd", f"if={image_path}", f"of={device_path}", "bs=16M", "status=progress"]
166
+ try:
167
+ # Start dd process with unbuffered output
168
+ process = subprocess.Popen(
169
+ dd_command,
170
+ stdout=subprocess.PIPE,
171
+ stderr=subprocess.PIPE,
172
+ text=True,
173
+ bufsize=1,
174
+ universal_newlines=True,
175
+ errors="replace"
176
+ )
177
+
178
+ # Regex to parse dd progress (more robust to handle variations)
179
+ pattern = re.compile(
180
+ r"(?:(?:dd: )?(\d+)\s+bytes(?:.*?)copied)|(?:^(\d+)\s+bytes\s+transferred)",
181
+ re.IGNORECASE
182
+ )
183
+
184
+ # Initialize tqdm progress bar
185
+ with tqdm(total=file_size, unit="B", unit_scale=True, desc="Copying", ncols=100) as pbar:
186
+ with open(debug_log, "w") as log_file:
187
+ while process.poll() is None:
188
+ rlist, _, _ = select.select([process.stdout, process.stderr], [], [], 0.1)
189
+ for stream in rlist:
190
+ line = stream.readline().strip()
191
+ if line:
192
+ log_file.write(f"{line}\n")
193
+ log_file.flush()
194
+ match = pattern.search(line)
195
+ if match:
196
+ bytes_transferred = int(match.group(1))
197
+ pbar.n = min(bytes_transferred, file_size)
198
+ pbar.refresh()
199
+ elif line:
200
+ click.echo(f"⚠️ dd: {line}") # Show other messages (e.g., errors)
201
+
202
+ # Capture remaining output and check for errors
203
+ stdout, stderr = process.communicate()
204
+ with open(debug_log, "a") as log_file:
205
+ if stdout.strip():
206
+ log_file.write(f"Final stdout: {stdout.strip()}\n")
207
+ if stderr.strip():
208
+ log_file.write(f"Final stderr: {stderr.strip()}\n")
209
+
210
+ if process.returncode != 0:
211
+ click.echo(f"❌ Failed to copy {image_path} to {device_path}: {stderr}")
212
+ click.echo(f"ℹ️ Check {debug_log} for raw dd output.")
213
+ sys.exit(1)
214
+
215
+ click.echo(f"✅ Successfully copied {image_path} to {device_path}")
216
+ subprocess.run(["sync"], check=True)
217
+ click.echo("✅ Synced data to device")
218
+ except subprocess.CalledProcessError as e:
219
+ click.echo(f"❌ Failed to copy {image_path} to {device_path}: {e.stderr}")
220
+ click.echo(f"ℹ️ Check {debug_log} for raw dd output.")
221
+ sys.exit(1)
222
+ except FileNotFoundError:
223
+ click.echo("❌ 'dd' not found. Ensure both are installed and accessible.")
224
+ sys.exit(1)
225
+
226
+ def write_bootimg(image_path):
227
+ """Write a boot image to a removable device."""
228
+ # Step 1: Validate image file
229
+ if not os.path.isfile(image_path):
230
+ click.echo(f"❌ Image file {image_path} does not exist.")
231
+ sys.exit(1)
232
+
233
+ click.echo(f"✅ Valid image file: {image_path}")
234
+
235
+ # Step 2: Check if dd is installed
236
+ if not check_dd_installed():
237
+ click.echo("⚠️ 'dd' is not installed on this system.")
238
+ if platform.system() == "Darwin":
239
+ click.echo("ℹ️ On macOS, 'dd' is included by default. Check your PATH or system configuration.")
240
+ elif platform.system() == "Linux":
241
+ click.echo("ℹ️ On Linux, install 'dd' using your package manager (e.g., 'sudo apt install coreutils' on Debian/Ubuntu).")
242
+ else:
243
+ click.echo("ℹ️ Please install 'dd' for your system.")
244
+ sys.exit(1)
245
+ click.echo("✅ 'dd' is installed")
246
+
247
+ # Step 3: Detect removable devices
248
+ devices = list_removable_devices() # Assumes this function exists
249
+ if not devices:
250
+ click.echo("⚠️ No removable devices detected. Please plug in your USB drive or SD card.")
251
+ sys.exit(1)
252
+
253
+ # Step 4: Display devices
254
+ click.echo("\n🔍 Detected removable devices:")
255
+ for i, dev in enumerate(devices):
256
+ click.echo(f" [{i}] Name: {dev['name']}, Size: {dev['size']} GB, Path: {dev['path']}")
257
+
258
+ # Step 5: Select device
259
+ selected_device = None
260
+ if len(devices) == 1:
261
+ confirm = input(f"\n✅ Do you want to use device {devices[0]['path']}? (y/N): ").strip().lower()
262
+ if confirm == 'y':
263
+ selected_device = devices[0]
264
+ else:
265
+ click.echo("❌ Operation cancelled by user.")
266
+ sys.exit(0)
267
+ else:
268
+ try:
269
+ selection = int(input("\n🔢 Multiple devices found. Enter the number of the device to use: ").strip())
270
+ if 0 <= selection < len(devices):
271
+ selected_device = devices[selection]
272
+ else:
273
+ click.echo("❌ Invalid selection.")
274
+ sys.exit(1)
275
+ except ValueError:
276
+ click.echo("❌ Invalid input. Please enter a number.")
277
+ sys.exit(1)
278
+
279
+ click.echo(f"\n🚀 Proceeding with device: {selected_device['path']}")
280
+
281
+ # Step 6: Unmount the selected device
282
+ unmount_device(selected_device['path'])
283
+
284
+ # Step 7: Copy the image to the device
285
+ copy_image_to_device(image_path, selected_device['path'])
286
+
287
+ # Step 8: Eject the device (platform-dependent)
288
+ try:
289
+ if platform.system() == "Darwin":
290
+ subprocess.run(["diskutil", "eject", selected_device['path']], check=True, capture_output=True, text=True)
291
+ click.echo(f"✅ Ejected {selected_device['path']} on macOS")
292
+ elif platform.system() == "Linux":
293
+ click.echo("ℹ️ Linux does not require explicit eject. Device is ready.")
294
+ else:
295
+ click.echo("ℹ️ Please manually eject the device if required.")
296
+ except subprocess.CalledProcessError as e:
297
+ click.echo(f"⚠️ Failed to eject {selected_device['path']}: {e.stderr}")
298
+ click.echo("ℹ️ Please manually eject the device.")
299
+
300
+
301
+ def write_image(version: str, board: str, swtype: str, internal: bool = False):
302
+ """
303
+ Download and write a bootable firmware image to a removable storage device.
304
+
305
+ Parameters:
306
+ version (str): Firmware version to download (e.g., "1.6.0").
307
+ board (str): Target board type, e.g., "modalix" or "mlsoc".
308
+ swtype (str): Software image type, e.g., "yocto" or "exlr".
309
+ internal (bool): Whether to use internal download sources. Defaults to False.
310
+
311
+ Raises:
312
+ RuntimeError: If the download or write process fails.
313
+ """
314
+ try:
315
+ click.echo(f"⬇️ Downloading boot image for version: {version}, board: {board}, swtype: {swtype}")
316
+ file_list = download_image(version, board, swtype, internal, update_type='bootimg')
317
+ if not isinstance(file_list, list):
318
+ raise ValueError("Expected list of extracted files, got something else.")
319
+
320
+ wic_file = next((f for f in file_list if f.endswith(".wic")), None)
321
+ if not wic_file:
322
+ raise FileNotFoundError("No .wic image file found after extraction.")
323
+
324
+ except Exception as e:
325
+ raise RuntimeError(f"❌ Failed to download image: {e}")
326
+
327
+ try:
328
+ click.echo(f"📝 Writing image to removable media: {wic_file}")
329
+ write_bootimg(wic_file)
330
+ except Exception as e:
331
+ raise RuntimeError(f"❌ Failed to write image: {e}")
332
+
333
+
334
+ if __name__ == "__main__":
335
+ if len(sys.argv) != 2:
336
+ click.echo("❌ Usage: python write_bootimg.py <image_file>")
337
+ sys.exit(1)
338
+
339
+ write_image('1.7.0', 'modalix', 'davinci', True)
sima_cli/update/remote.py CHANGED
@@ -83,7 +83,8 @@ def get_remote_board_info(ip: str, passwd: str = DEFAULT_PASSWORD) -> Tuple[str,
83
83
 
84
84
  return board_type, build_version
85
85
 
86
- except Exception:
86
+ except Exception as e:
87
+ click.echo(f"Unable to retrieve board info {e}")
87
88
  return "", ""
88
89
 
89
90
 
@@ -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,12 @@ 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
+
126
148
  extracted_paths = []
127
149
 
128
150
  try:
@@ -140,14 +162,29 @@ def _extract_required_files(tar_path: str, board: str) -> list:
140
162
  if os.path.exists(full_dest_path):
141
163
  click.echo(f"⚠️ Skipping existing file: {full_dest_path}")
142
164
  extracted_paths.append(full_dest_path)
143
- continue
165
+ else:
166
+ os.makedirs(os.path.dirname(full_dest_path), exist_ok=True)
167
+ tar.extract(member, path=extract_dir)
168
+ click.echo(f"✅ Extracted: {full_dest_path}")
169
+ extracted_paths.append(full_dest_path)
144
170
 
145
- # Ensure directory structure exists
146
- os.makedirs(os.path.dirname(full_dest_path), exist_ok=True)
171
+ # If it's a .wic.gz file, decompress it now
172
+ if full_dest_path.endswith(".wic.gz"):
173
+ uncompressed_path = full_dest_path[:-3] # remove .gz
147
174
 
148
- tar.extract(member, path=extract_dir)
149
- extracted_paths.append(full_dest_path)
150
- click.echo(f"✅ Extracted: {full_dest_path}")
175
+ if os.path.exists(uncompressed_path):
176
+ click.echo(f"⚠️ Skipping decompression: {uncompressed_path} already exists")
177
+ extracted_paths.append(uncompressed_path)
178
+ continue
179
+
180
+ try:
181
+ with gzip.open(full_dest_path, 'rb') as f_in:
182
+ with open(uncompressed_path, 'wb') as f_out:
183
+ shutil.copyfileobj(f_in, f_out)
184
+ click.echo(f"📦 Decompressed: {uncompressed_path}")
185
+ extracted_paths.append(uncompressed_path)
186
+ except Exception as decomp_err:
187
+ click.echo(f"❌ Failed to decompress {full_dest_path}: {decomp_err}")
151
188
 
152
189
  if not extracted_paths:
153
190
  click.echo("⚠️ No matching files were found or extracted.")
@@ -158,7 +195,8 @@ def _extract_required_files(tar_path: str, board: str) -> list:
158
195
  click.echo(f"❌ Failed to extract files from archive: {e}")
159
196
  return []
160
197
 
161
- def _download_image(version_or_url: str, board: str, internal: bool = False):
198
+
199
+ def _download_image(version_or_url: str, board: str, internal: bool = False, update_type: str = 'standard'):
162
200
  """
163
201
  Download or use a firmware image for the specified board and version or file path.
164
202
 
@@ -176,7 +214,7 @@ def _download_image(version_or_url: str, board: str, internal: bool = False):
176
214
  # Case 1: Local file provided
177
215
  if os.path.exists(version_or_url) and os.path.isfile(version_or_url):
178
216
  click.echo(f"📁 Using local firmware file: {version_or_url}")
179
- return _extract_required_files(version_or_url, board)
217
+ return _extract_required_files(version_or_url, board, update_type)
180
218
 
181
219
  # Case 2: Treat as custom full URL
182
220
  if version_or_url.startswith("http://") or version_or_url.startswith("https://"):
@@ -198,7 +236,7 @@ def _download_image(version_or_url: str, board: str, internal: bool = False):
198
236
  firmware_path = download_file_from_url(image_url, dest_path, internal=internal)
199
237
 
200
238
  click.echo(f"📦 Firmware downloaded to: {firmware_path}")
201
- return _extract_required_files(firmware_path, board)
239
+ return _extract_required_files(firmware_path, board, update_type)
202
240
 
203
241
  except Exception as e:
204
242
  click.echo(f"❌ Host update failed: {e}")
@@ -328,6 +366,27 @@ def _update_remote(extracted_paths: List[str], ip: str, board: str, passwd: str,
328
366
 
329
367
  return script_path
330
368
 
369
+ def download_image(version_or_url: str, board: str, swtype: str, internal: bool = False, update_type: str = 'standard'):
370
+ """
371
+ Download and extract a firmware image for a specified board.
372
+
373
+ Args:
374
+ version_or_url (str): Either a version string (e.g., "1.6.0") or a direct URL or local file path to the image.
375
+ board (str): The board type (e.g., "mlsoc", "modalix").
376
+ swtype (str): The software type (default to 'davinci', possible values: `davinci`, `modalix`): not supported for now
377
+ internal (bool): Whether to use internal download paths (e.g., Artifactory).
378
+ update_type (str): Whether this is standard update or writing boot image.
379
+
380
+ Returns:
381
+ List[str]: Paths to the extracted image files.
382
+ """
383
+
384
+ if 'http' not in version_or_url and not os.path.exists(version_or_url):
385
+ version_or_url = _pick_from_available_versions(board, version_or_url, internal)
386
+
387
+ extracted_paths = _download_image(version_or_url, board, internal, update_type)
388
+ return extracted_paths
389
+
331
390
  def perform_update(version_or_url: str, ip: str = None, internal: bool = False, passwd: str = "edgeai", auto_confirm: bool = False):
332
391
  r"""
333
392
  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
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sima-cli
3
- Version: 0.0.18
3
+ Version: 0.0.19
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,33 +1,39 @@
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=2xNp40YbbA9TjpFNNqUbe-Cjo-Xe8LvBbe2706ROs3k,49
4
+ sima_cli/cli.py,sha256=k4w4NB6yfqjTrNBEv338yQTBWIMZ-ul7W-0vo7y01dc,11326
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=VT8C1_EX-DW-n7DuxI9d0vPb87JBX-Rg9Bw9JHh1aeo,3108
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
19
23
  sima_cli/update/__init__.py,sha256=0P-z-rSaev40IhfJXytK3AFWv2_sdQU4Ry6ei2sEus0,66
24
+ sima_cli/update/bmaptool.py,sha256=KrhUGShBwY4Wzz50QiuMYAxxPgEy1nz5C68G-0a4qF4,4988
25
+ sima_cli/update/bootimg.py,sha256=iMe8b42JK0KF-udSB6Kl044XUJtNgMUmBNxyuWneAkU,13387
20
26
  sima_cli/update/local.py,sha256=CyUFLs5Lz5w4VyM6ip4wndKBBLz3_KZ-3scEvSiOrcg,3299
21
27
  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
28
+ sima_cli/update/remote.py,sha256=DvcvUX1ydpyF4DwUsVXagb7My6H2MNoGiCQfGfO06iI,8939
29
+ sima_cli/update/updater.py,sha256=SprOcvSzRSwViGd2dYTtyTQfiwdLQcqZoS3KBQpxz4s,18466
24
30
  sima_cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
31
  sima_cli/utils/artifactory.py,sha256=6YyVpzVm8ATy7NEwT9nkWx-wptkXrvG7Wl_zDT6jmLs,2390
26
32
  sima_cli/utils/config.py,sha256=wE-cPQqY_gOqaP8t01xsRHD9tBUGk9MgBUm2GYYxI3E,1616
27
33
  sima_cli/utils/config_loader.py,sha256=7I5we1yiCai18j9R9jvhfUzAmT3OjAqVK35XSLuUw8c,2005
28
- sima_cli/utils/env.py,sha256=INOAVfCFwh0BHeqSMmEHXfpIyepNha4-SMI1iOOplOo,5795
34
+ sima_cli/utils/env.py,sha256=Jrb062EnpMBr1jGMotBlI2j9LEH6W1Z5Tgt6LHY7yYQ,5753
29
35
  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
36
+ sima_cli-0.0.19.dist-info/licenses/LICENSE,sha256=a260OFuV4SsMZ6sQCkoYbtws_4o2deFtbnT9kg7Rfd4,1082
31
37
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
38
  tests/test_app_zoo.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
39
  tests/test_auth.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -36,8 +42,8 @@ tests/test_download.py,sha256=t87DwxlHs26_ws9rpcHGwr_OrcRPd3hz6Zmm0vRee2U,4465
36
42
  tests/test_firmware.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
43
  tests/test_model_zoo.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
44
  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,,
45
+ sima_cli-0.0.19.dist-info/METADATA,sha256=7_B7RJl70GRcQOHvMQRrxhjvQhuQD9pqX3FUQvKRr1M,3631
46
+ sima_cli-0.0.19.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
47
+ sima_cli-0.0.19.dist-info/entry_points.txt,sha256=xRYrDq1nCs6R8wEdB3c1kKuimxEjWJkHuCzArQPT0Xk,47
48
+ sima_cli-0.0.19.dist-info/top_level.txt,sha256=FtrbAUdHNohtEPteOblArxQNwoX9_t8qJQd59fagDlc,15
49
+ sima_cli-0.0.19.dist-info/RECORD,,