Import("env") # noqa: F821 from pathlib import Path import hashlib import shutil import subprocess project_dir = Path(env.subst("$PROJECT_DIR")) # noqa: F821 ui_dir = project_dir / "ui" generated_dir = project_dir / "src" / "generated" generated_dir.mkdir(parents=True, exist_ok=True) npm = shutil.which("npm") if npm and (ui_dir / "node_modules").is_dir(): subprocess.run([npm, "run", "build"], cwd=ui_dir, check=True) assets = { "indexHtml": ui_dir / "dist" / "index.html", "appCss": ui_dir / "dist" / "styles.min.css", "appJs": ui_dir / "dist" / "app.min.js", } if not all(path.is_file() for path in assets.values()): assets = { "indexHtml": ui_dir / "index.html", "appCss": ui_dir / "styles.css", "appJs": ui_dir / "app.js", } missing = [str(path) for path in assets.values() if not path.is_file()] if missing: raise SystemExit( "Missing UI assets: " + ", ".join(missing) ) def emit_array(name, data): lines = [f"const char {name}[] PROGMEM = {{"] for offset in range(0, len(data), 16): chunk = data[offset : offset + 16] values = ", ".join(f"0x{value:02X}" for value in chunk) lines.append(f" {values},") lines.append(" 0x00") lines.append("};") return "\n".join(lines) def emit_length(name, data): return f"const std::size_t {name}Length = {len(data)}U;" header_lines = [ "#pragma once", "", "#include ", "#include ", "", "#include ", "", ] source_lines = [ "// Generated by scripts/embed_ui.py. Do not edit manually.", "#include \"UiAssets.h\"", "", "#include ", "", ] for name, path in assets.items(): data = path.read_bytes() digest = hashlib.sha256(data).hexdigest() header_lines.append(f"extern const char {name}[] PROGMEM;") header_lines.append(f"extern const std::size_t {name}Length;") header_lines.append(f"extern const char {name}Sha256[] PROGMEM;") source_lines.extend( [ emit_array(name, data), emit_length(name, data), f'const char {name}Sha256[] PROGMEM = "{digest}";', "", ] ) def write_if_changed(path, content): if path.is_file() and path.read_text(encoding="utf-8") == content: return path.write_text(content, encoding="utf-8") write_if_changed(generated_dir / "UiAssets.h", "\n".join(header_lines) + "\n") write_if_changed(generated_dir / "UiAssets.cpp", "\n".join(source_lines) + "\n") print("Embedded UI assets:") for name, path in assets.items(): data = path.read_bytes() print(f" {name}: {path.name} ({len(data)} bytes)")