updates
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Render the real LVGL pages with demo telemetry using ESPHome's headless SDL host."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
from PIL import Image
|
||||
import yaml
|
||||
|
||||
from generate import Dumper, HERE, Lambda, generate
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--output', type=Path, default=HERE / 'previews')
|
||||
args = parser.parse_args()
|
||||
printers = yaml.safe_load((HERE / 'printers.yaml').read_text())['printers']
|
||||
package = generate(printers)
|
||||
demo_values = {'progress': 67, 'nozzle': 218, 'nozzle_target': 220, 'bed': 60,
|
||||
'bed_target': 60, 'time_left': 1.75, 'layer': 134, 'total_layers': 200,
|
||||
'fan': 80, 'left_nozzle': 219, 'left_target': 220,
|
||||
'right_nozzle': 37, 'right_target': 0}
|
||||
demo_text = {'status': 'running', 'task': 'Workshop organizer - plate 2',
|
||||
'stage': 'printing', 'online': 'on', 'print_error': 'off', 'hms_error': 'off',
|
||||
'remaining_unit': 'h', 'nozzle_unit': '°C', 'bed_unit': '°C'}
|
||||
colors = ['#60DE9AFF', '#F4C96BFF', '#74A9E8FF', '#F8F4ECFF',
|
||||
'#DF8BBFFF', '#FA936BFF', '#8E79CAFF', '#4CC9C1FF']
|
||||
for printer in printers:
|
||||
prefix = printer['id'] + '_'
|
||||
for section in ('sensor', 'text_sensor'):
|
||||
for entry in package[section]:
|
||||
if not entry['id'].startswith(prefix):
|
||||
continue
|
||||
key = entry['id'][len(prefix):]
|
||||
if section == 'sensor':
|
||||
value = str(demo_values[key])
|
||||
else:
|
||||
if key.startswith('tray_'):
|
||||
_, index, attribute = key.split('_')
|
||||
text = {'type': 'PLA', 'color': colors[int(index) % len(colors)],
|
||||
'active': 'on' if index == '0' else 'off'}[attribute]
|
||||
else:
|
||||
text = demo_text[key]
|
||||
value = 'std::string(' + json.dumps(text, ensure_ascii=False) + ')'
|
||||
entry.clear()
|
||||
entry.update({'platform': 'template', 'id': prefix + key, 'internal': True,
|
||||
'lambda': Lambda('return ' + value + ';'), 'update_interval': '60s'})
|
||||
for script in package['script']:
|
||||
code = script['then'][0]['lambda']
|
||||
script['then'][0]['lambda'] = Lambda(code.replace(
|
||||
'id(ha_api).is_connected_with_state_subscription()', 'true'))
|
||||
|
||||
args.output.mkdir(parents=True, exist_ok=True)
|
||||
with tempfile.TemporaryDirectory(prefix='bambu-lvgl-preview-') as work:
|
||||
root = Path(work)
|
||||
# Inject real LVGL pointer samples; test bubbling through cards/buttons
|
||||
# without a GUI window or a physical touchscreen.
|
||||
touch_header = root / 'preview_touch.h'
|
||||
touch_header.write_text('''#pragma once
|
||||
#include <lvgl.h>
|
||||
namespace bambu_preview {
|
||||
inline int x = 0, y = 0;
|
||||
inline bool pressed = false;
|
||||
inline void setup() {
|
||||
auto *input = lv_indev_create();
|
||||
lv_indev_set_type(input, LV_INDEV_TYPE_POINTER);
|
||||
lv_indev_set_display(input, lv_display_get_default());
|
||||
lv_indev_set_read_cb(input, [](lv_indev_t *, lv_indev_data_t *data) {
|
||||
data->point.x = x;
|
||||
data->point.y = y;
|
||||
data->state = pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
||||
});
|
||||
}
|
||||
}
|
||||
''')
|
||||
steps = [{'component.update': entry['id']}
|
||||
for section in ('sensor', 'text_sensor') for entry in package[section]]
|
||||
steps += [{'delay': '1s'}]
|
||||
for printer in printers:
|
||||
steps += [{'lvgl.page.show': printer['id'] + '_page'},
|
||||
{'script.execute': printer['id'] + '_refresh'}, {'delay': '300ms'},
|
||||
{'snapshot.take': {'id': 'panel_display', 'filename': printer['id'] + '.bmp'}}]
|
||||
steps += [{'lvgl.page.show': printers[0]['id'] + '_page'}, {'delay': '250ms'},
|
||||
{'lambda': Lambda('bambu_preview::setup();')}]
|
||||
if len(printers) > 1:
|
||||
# First -> second -> first -> last -> first, then a swipe starting
|
||||
# on NEXT must advance exactly once even when released over PREV.
|
||||
gestures = [(650, 180, 180, printers[1]['id']),
|
||||
(180, 650, 180, printers[0]['id']),
|
||||
(180, 650, 180, printers[-1]['id']),
|
||||
(650, 180, 180, printers[0]['id']),
|
||||
(730, 60, 460, printers[1]['id'])]
|
||||
for start, end, y, expected in gestures:
|
||||
steps += [
|
||||
{'lambda': Lambda(f'bambu_preview::x = {start}; bambu_preview::y = {y}; bambu_preview::pressed = true;')},
|
||||
{'delay': '70ms'},
|
||||
{'lambda': Lambda(f'bambu_preview::x = {end};')}, {'delay': '70ms'},
|
||||
{'lambda': Lambda('bambu_preview::pressed = false;')}, {'delay': '350ms'},
|
||||
{'lambda': Lambda(f'if (!id({expected}_page).is_showing()) {{ ESP_LOGE("preview", "Swipe navigation failed"); exit(2); }}')},
|
||||
]
|
||||
steps += [{'lambda': Lambda('exit(0);')}]
|
||||
config = {'esphome': {'name': 'bambu-display-preview',
|
||||
'includes': [str(HERE / 'dashboard.h'), str(touch_header)],
|
||||
'on_boot': {'priority': -100, 'then': steps}},
|
||||
'host': {}, 'logger': {'level': 'WARN'},
|
||||
'display': [{'platform': 'sdl', 'id': 'panel_display', 'dimensions': '800x480',
|
||||
'headless': True, 'auto_clear_enabled': False, 'update_interval': 'never'}],
|
||||
'lvgl': {'id': 'dashboard', 'displays': ['panel_display'], 'buffer_size': '100%',
|
||||
'color_depth': 16, 'bg_color': 0x0D131B, 'text_color': 0xEDF3F7,
|
||||
'default_font': 'montserrat_18', **package.pop('lvgl')}, **package}
|
||||
filename = root / 'preview.yaml'
|
||||
filename.write_text(yaml.dump(config, Dumper=Dumper, sort_keys=False, allow_unicode=True))
|
||||
subprocess.run(['esphome', 'compile', str(filename)], check=True)
|
||||
binary = root / '.esphome/build/bambu-display-preview/.pioenvs/bambu-display-preview/program'
|
||||
subprocess.run([str(binary)], check=True, timeout=30, cwd=root)
|
||||
for printer in printers:
|
||||
bmp = root / '.esphome/snapshots/bambu-display-preview' / (printer['id'] + '.bmp')
|
||||
with Image.open(bmp) as image:
|
||||
image.save(args.output / (printer['id'] + '.png'))
|
||||
print(f'Saved {len(printers)} demo screenshots to {args.output}')
|
||||
print('Passed LVGL pointer swipe tests, including wraparound and button gesture consumption.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user