#!/usr/bin/env python3 """Generate LVGL colour icon descriptors from Twemoji PNGs. Downloads the Twemoji 72x72 PNGs for the glyphs the Lisael Box UI needs, downscales them (Pillow/Lanczos) and converts each to an LVGL RGB565A8 `lv_image_dsc_t` C source in ``main/ui/assets/``. Run with a Python that has both Pillow and pypng available, e.g. the ESP-IDF venv: ~/.espressif/python_env/idf5.4_py3.14_env/bin/python tools/gen_icons.py Twemoji artwork is CC-BY 4.0 (https://github.com/jdecked/twemoji). """ import os import ssl import subprocess import sys import urllib.request # name -> (twemoji codepoint, target size px) SPECS = { # home mode tiles "radio": ("1f4fb", 50), "sound": ("1f50a", 50), "calm": ("1fae7", 50), "clock": ("1f550", 50), "games": ("1f3ae", 50), # weather (open-meteo WMO codes) "wsun": ("2600", 42), "wpartly": ("26c5", 42), "wfog": ("1f32b", 42), "wdrizzle": ("1f326", 42), "wrain": ("1f327", 42), "wsleet": ("1f328", 42), "wsnow": ("2744", 42), "wthunder": ("26c8", 42), "wunknown": ("2753", 42), "book": ("1f4d6", 50), # "Lire" home tile (open book). Word icons for the # reading game are generated to the SD by gen_lire_content.py. "reglages": ("2699", 50), # "Réglages WiFi" home tile (gear) "routine": ("1f9f9", 50), # "Routine" home tile (broom 🧹) — chores/steps } BASE = "https://raw.githubusercontent.com/jdecked/twemoji/main/assets/72x72" HERE = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.dirname(HERE) OUT = os.path.join(ROOT, "main", "ui", "assets") CONV = os.path.join(ROOT, "managed_components", "lvgl__lvgl", "scripts", "LVGLImage.py") TMP = "/tmp" def main(): from PIL import Image os.makedirs(OUT, exist_ok=True) ctx = ssl.create_default_context() ok, bad = [], [] for name, (cp, sz) in SPECS.items(): try: req = urllib.request.Request(f"{BASE}/{cp}.png", headers={"User-Agent": "curl/8"}) data = urllib.request.urlopen(req, timeout=15, context=ctx).read() src = os.path.join(TMP, f"em_{name}.png") open(src, "wb").write(data) im = Image.open(src).convert("RGBA").resize((sz, sz), Image.LANCZOS) rs = os.path.join(TMP, f"lisael_ic_{name}.png") im.save(rs) r = subprocess.run( [sys.executable, CONV, "--ofmt", "C", "--cf", "RGB565A8", "--name", f"lisael_ic_{name}", "-o", OUT, rs], capture_output=True, text=True) (ok if r.returncode == 0 else bad).append(name) if r.returncode != 0: print("CONV FAIL", name, r.stderr[-200:]) except Exception as e: # noqa: BLE001 bad.append(name) print("FAIL", name, str(e)[:200]) print(f"converted {len(ok)}/{len(SPECS)}", "bad:", bad) if __name__ == "__main__": main()