Public Access
- Home Assistant attributes can be paths into lists (forecast.1.condition). - Date elements can show a day ahead (day offset 0-7). - Pages hold 16 elements (page format 6; the old formats are frozen at 8). Pages are reset in place (clearPage), not through a 4 KB temporary. - Verwachting page: today (icon, temperature, max/min) and the next 2 days with 7 x 7 icons (weer7 <condition>), from a template sensor (docs/home-assistant.md). - examples/home-pages.json is exported from the display with all pages and their icons, private entity id parts replaced by placeholders (tools/export_examples.py replaces tools/make_example_pages.py). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
91 lines
3.6 KiB
Python
91 lines
3.6 KiB
Python
"""Writes examples/home-pages.json from the pages on a display: the dynamic pages with the
|
|
icons, bitmaps and fonts they use, as a backup file that the web UI imports (System tab).
|
|
|
|
python tools/export_examples.py [ESP32 address, default 10.1.1.198]
|
|
|
|
Private parts of entity ids are replaced by placeholders (PRIVATE below); the export stops
|
|
when a page still contains one of them. The pages that come with the firmware (Clock) and
|
|
their icons (the Pikachus) are left out: every display has them. Only the Python standard
|
|
library is used.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
import urllib.parse
|
|
import urllib.request
|
|
|
|
ESP = 'http://' + (sys.argv[1] if len(sys.argv) > 1 else '10.1.1.198')
|
|
OUT = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'examples', 'home-pages.json')
|
|
|
|
# Private text -> placeholder (in entity ids, attributes and texts)
|
|
PRIVATE = {
|
|
'vigilantestraat': 'home', # the street (climate entity)
|
|
'122225067403': '000000000000', # the Envoy's serial number
|
|
'mangavalkserver': 'plex_server', # the Plex server's name
|
|
}
|
|
FROM_FIRMWARE_PAGES = {'clock'}
|
|
FROM_FIRMWARE_ICONS = {'pikachu', 'pikachu yawning'}
|
|
|
|
|
|
def get(path):
|
|
with urllib.request.urlopen(ESP + path, timeout=60) as r:
|
|
return json.loads(r.read())
|
|
|
|
|
|
def clean(value):
|
|
if isinstance(value, str):
|
|
for private, placeholder in PRIVATE.items():
|
|
value = re.sub(re.escape(private), placeholder, value, flags=re.IGNORECASE)
|
|
return value
|
|
|
|
|
|
def main():
|
|
pages = [p for p in get('/api/pages')['pages'] if p['name'].lower() not in FROM_FIRMWARE_PAGES]
|
|
icons = {i['name'].lower(): i for i in get('/api/icons')['icons']}
|
|
bitmaps = {b['name'].lower(): b for b in get('/api/bitmaps')['bitmaps'] if not b['builtin']}
|
|
fonts = {f['name'].lower(): f for f in get('/api/fonts')['fonts'] if not f['builtin']}
|
|
|
|
# What the pages use: backgrounds, fixed images, the images by state (name start), fonts
|
|
images, used_fonts = set(), set()
|
|
for p in pages:
|
|
for s in p['page']['slots']:
|
|
images.add(s['bitmap'].lower())
|
|
for e in p['page']['elements']:
|
|
used_fonts.add(e['font'].lower())
|
|
if e['type'] != 'image':
|
|
continue
|
|
if not e['entity']:
|
|
images.add(e['text'].lower())
|
|
else:
|
|
start = e['text'].lower()
|
|
images.update(n for n in list(icons) + list(bitmaps) if n.startswith(start))
|
|
for e in p['page']['elements']:
|
|
for k, v in e.items():
|
|
e[k] = clean(v)
|
|
|
|
out = {
|
|
'format': 'flipdot-backup', 'version': 1, 'firmware': get('/api/status')['version'],
|
|
'exported': '2026-10-16T00:00:00Z',
|
|
'bitmaps': [{'name': b['name'], 'data': b['data']} for n, b in sorted(bitmaps.items()) if n in images],
|
|
'icons': [{k: i[k] for k in ('name', 'width', 'height', 'data')} for n, i in sorted(icons.items())
|
|
if n in images and n not in FROM_FIRMWARE_ICONS],
|
|
'pages': [{'name': p['name'], 'page': p['page']} for p in pages],
|
|
'fonts': [],
|
|
}
|
|
for n in sorted(used_fonts & set(fonts)):
|
|
out['fonts'].append(get('/api/font?name=' + urllib.parse.quote(fonts[n]['name'])))
|
|
|
|
text = json.dumps(out, ensure_ascii=False, indent=1)
|
|
left = [p for p in PRIVATE if p.lower() in text.lower()]
|
|
if left:
|
|
sys.exit('Private text still in the export: ' + ', '.join(left))
|
|
with open(OUT, 'w', encoding='utf-8', newline='\n') as f:
|
|
f.write(text + '\n')
|
|
print(len(out['pages']), 'pages,', len(out['icons']), 'icons,', len(out['bitmaps']), 'bitmaps,',
|
|
len(out['fonts']), 'fonts ->', OUT)
|
|
|
|
|
|
main()
|