Public Access
Weather forecast page, 16 elements per page, attribute paths, examples from the display
- 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>
This commit is contained in:
1 parent
839e86e8f1
commit
a6b946e17c
33 files changed
+1918
-1050
No files matched your search
@@ -0,0 +1,90 @@
|
||||
"""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()
|
||||
Reference in new issue
Block a user