Home is a grid of arrangeable widgets (24 kinds, presets, a layout per orientation, Lua app widgets via wada.widget); Unread, Chat and Mentions get a large size. Theme > Background puts an aurora (still by default, or moving), a colour, a picture or nothing behind every screen, with glass cards and a smoked-glass top bar. A GPS icon in the bar, a GPS battery saver (u-blox GNSS stop/start where there is no power switch), and a battery saver that no longer waits for Bluetooth and Wi-Fi to be off. Also: navigation bar position, every settings page on the redesign's cards, a faster app drawer, slower screensaver drift for burn-in, text wrapping at spaces only, the V4-R8 Store Apps tab, and SDK Test marked extended-SDK only (keeps the 2 MB V4 at 99.8%). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
91 lines
4.2 KiB
Python
91 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""Bake the catalog's Lua apps into the firmware as seeded built-ins.
|
|
|
|
The Lua host already supports this: luaAppLaunchFile(id, title, embedded, len)
|
|
tries <data>/apps/<id>.lua first and falls back to an embedded source when the
|
|
file is not there. That file-first order is what makes a seeded app updatable —
|
|
download a newer version from the Store and the file simply wins.
|
|
|
|
This generates src/ui-touch/lua_builtin.h from deploy/apps/, i.e. exactly the
|
|
sources the Store serves, so a board that cannot reach the Store still ships
|
|
with the apps rather than an empty drawer.
|
|
|
|
It used to read out/firmware/apps/ instead. Nothing writes that directory --
|
|
deploy-apps.sh rsyncs deploy/apps/ straight to the VPS -- so it was a stale
|
|
mirror, and the "same sources the Store serves" guarantee was false: a bumped
|
|
app shipped to the Store while the baked-in copy stayed on whatever version was
|
|
mirrored last (#257).
|
|
|
|
Run from the repo root: python3 scripts/build/gen-lua-builtin.py
|
|
"""
|
|
import json, os, re, sys
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
APPS = os.path.join(ROOT, 'deploy/apps') # canonical; what deploy-apps.sh publishes
|
|
OUT = os.path.join(ROOT, 'src/ui-touch/lua_builtin.h')
|
|
DELIM = 'WADALUA' # raw-string delimiter: keeps Lua source verbatim
|
|
|
|
|
|
def main():
|
|
cat_path = os.path.join(APPS, 'apps.json')
|
|
if not os.path.exists(cat_path):
|
|
sys.exit('no catalog at ' + cat_path)
|
|
cat = json.load(open(cat_path, encoding='utf-8'))['apps']
|
|
|
|
entries = []
|
|
for app in cat:
|
|
aid, ver, name = app['id'], app['ver'], app['name']
|
|
# "seed": false keeps an app out of the image while still publishing it to
|
|
# the Store. Seeding is not free: it is flash on the boards that have the
|
|
# least of it, which are exactly the CAP_BUILTIN_LUA_APPS boards. An app
|
|
# that is large, or that needs hardware those boards do not have, earns its
|
|
# place rather than getting it by being in the catalog.
|
|
if app.get('seed') is False:
|
|
print(' skip %s: seed=false in apps.json' % aid)
|
|
continue
|
|
src = os.path.join(APPS, aid, ver, aid + '.lua')
|
|
if not os.path.exists(src):
|
|
print(' skip %s: no %s' % (aid, os.path.relpath(src, ROOT)))
|
|
continue
|
|
code = open(src, encoding='utf-8').read()
|
|
if (')' + DELIM + '"') in code:
|
|
sys.exit('%s contains the raw-string delimiter' % aid)
|
|
# An app that needs the extended SDK cannot run on a board without it, so it
|
|
# is baked in only where it can (CAP_LUA_SDK_EXT, every board but the 2 MB
|
|
# Heltec V4, the one with the least flash to give).
|
|
entries.append((aid, name, ver, code, app.get('requires') == 'sdk_ext'))
|
|
|
|
if not entries:
|
|
sys.exit('no app sources found under ' + APPS)
|
|
|
|
w = ['// Generated by scripts/build/gen-lua-builtin.py — DO NOT EDIT.',
|
|
'// Source: deploy/apps/ (the same .lua the Lua Store serves).',
|
|
'// Seeded built-ins: a downloaded <data>/apps/<id>.lua always wins.',
|
|
'#pragma once', '',
|
|
'struct LuaBuiltinApp { const char* id; const char* name; const char* ver; const char* src; };', '']
|
|
for aid, name, ver, code, ext in entries:
|
|
if ext: w.append('#if CAP_LUA_SDK_EXT')
|
|
w.append('static const char kLuaSrc_%s[] = R"%s(%s)%s";' % (aid, DELIM, code, DELIM))
|
|
if ext: w.append('#endif')
|
|
w.append('')
|
|
w.append('static const LuaBuiltinApp kLuaBuiltin[] = {')
|
|
for aid, name, ver, _, ext in entries:
|
|
if ext: w.append('#if CAP_LUA_SDK_EXT')
|
|
w.append(' { "%s", "%s", "%s", kLuaSrc_%s },' % (aid, name, ver, aid))
|
|
if ext: w.append('#endif')
|
|
w.append('};')
|
|
w.append('static const int kLuaBuiltinCount = (int)(sizeof(kLuaBuiltin)/sizeof(kLuaBuiltin[0]));')
|
|
w.append('')
|
|
|
|
open(OUT, 'w', encoding='utf-8').write('\n'.join(w))
|
|
total = sum(len(e[3]) for e in entries)
|
|
print('%s: %d apps, %d bytes of Lua' %
|
|
(os.path.relpath(OUT, ROOT), len(entries), total))
|
|
for aid, _, ver, code, ext in entries:
|
|
print(' %-10s v%-4s %6d B%s' % (aid, ver, len(code), ' (extended SDK only)' if ext else ''))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|