Files
L'électron rare 21bfd2f7de feat(box): wire routine mode + tile
Add LISAEL_MODE_ROUTINE to the UI shell: enum entry, build_screen()
case, home carousel tile (8th, broom icon U+1F9F9, periwinkle pastel),
minimal navigable stub in modes/routine.c, CMakeLists srcs update,
Twemoji icon generated (17/17 ok). s_adots[8] now at capacity.
2026-06-21 10:53:17 +02:00

75 lines
2.9 KiB
Python

#!/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()