From 360e86e46a93b8d43198f30038da984ed8d0a8d7 Mon Sep 17 00:00:00 2001 From: Joshua King Date: Tue, 20 Oct 2026 15:13:24 -0400 Subject: [PATCH] Show each printer on the fleet page as a status gauge. Co-authored-by: Cursor --- esphome/bambu-display/README.md | 15 +- esphome/bambu-display/dashboard.h | 17 +- esphome/bambu-display/generate.py | 84 +- esphome/bambu-display/generated.yaml | 1097 ++++++++++-------------- esphome/bambu-display/preview.py | 2 +- esphome/bambu-display/test_generate.py | 20 +- 6 files changed, 537 insertions(+), 698 deletions(-) diff --git a/esphome/bambu-display/README.md b/esphome/bambu-display/README.md index 3de0baf..0a6c1bf 100644 --- a/esphome/bambu-display/README.md +++ b/esphome/bambu-display/README.md @@ -17,9 +17,12 @@ Home Assistant must be running and its printer integrations must have current pr The configured swipe order is: -1. Fleet — one row per printer: status, job name, progress, finish time and - time remaining. The header counts printers that need attention, are printing, - paused, idle, offline or still syncing. Tap a row to open that printer. +1. Fleet — one steam gauge per printer, matching the progress arcs on the + printer pages. The needle is print progress, and the arc color follows + status: green while printing, amber while paused, red for an error. The + center is the percent complete and the caption is the status. A line under + the gauges counts printers that need attention, are printing, paused, idle, + offline or still syncing. Tap a gauge to open that printer. 2. PrintsCharming — P1S, Office, both four-slot AMS units. 3. Polly-Pocket — A1 mini, Office, external spool. 4. Ds-Turd-Flinger — A1, Upstairs, four-slot AMS. @@ -36,8 +39,8 @@ The configured swipe order is: Swipe left for the next page or right for the previous page. Navigation wraps from the last printer back to the fleet page. Previous/Next buttons provide the -same navigation. Swipes propagate through the fleet rows, arc gauges and filament -slots. A swipe consumes the rest of that touch so dragging across a row or a +same navigation. Swipes propagate through the fleet gauges and filament +slots. A swipe consumes the rest of that touch so dragging across a gauge or a navigation button does not also click it. FDM pages show print status, job name, progress, nozzle and bed current/target @@ -146,7 +149,7 @@ python esphome/bambu-display/generate.py --check The generator requires PyYAML (already included in ESPHome's Python environment). Do not hand-edit `generated.yaml`. There is no two-printer limit in the generator; -adding a printer creates its own subscriptions, widgets, swipe page and fleet row. The practical +adding a printer creates its own subscriptions, widgets, swipe page and fleet gauge. The practical limit depends on the board's available RAM and flash, and adding printers requires regenerating and installing the firmware. Seven printers are configured; generation is also tested with nine. Each Bambu printer's filament strip supports up to eight slots. diff --git a/esphome/bambu-display/dashboard.h b/esphome/bambu-display/dashboard.h index f72e333..e8eb895 100644 --- a/esphome/bambu-display/dashboard.h +++ b/esphome/bambu-display/dashboard.h @@ -290,7 +290,7 @@ struct Widgets { lv_obj_t *elapsed = nullptr; }; struct OverviewWidgets { - lv_obj_t *status, *task, *meta, *progress, *accent; + lv_obj_t *arc, *value, *detail; }; inline void set_text(lv_obj_t *label, const std::string &text) { @@ -362,13 +362,14 @@ inline void render(const Snapshot &data, const Widgets &widgets) { inline void render_overview(const Snapshot &data, const OverviewWidgets &widgets) { const bool live = data.live(); const uint32_t color = data.status_color(); - set_text(widgets.status, data.status_text()); - lv_obj_set_style_text_color(widgets.status, lv_color_hex(color), 0); - if (widgets.task) set_text(widgets.task, data.overview_task()); - set_text(widgets.meta, data.overview_meta()); - lv_bar_set_value(widgets.progress, live ? gauge_value(data.progress, 100) : 0, LV_ANIM_OFF); - lv_obj_set_style_bg_color(widgets.progress, lv_color_hex(color), LV_PART_INDICATOR); - lv_obj_set_style_bg_color(widgets.accent, lv_color_hex(color), 0); + const float shown = live && std::isfinite(data.progress) + ? std::clamp(data.progress, 0.0f, 100.0f) : NAN; + lv_arc_set_value(widgets.arc, live ? gauge_value(data.progress, 100) : 0); + lv_obj_set_style_arc_color(widgets.arc, lv_color_hex(color), LV_PART_INDICATOR); + set_text(widgets.value, number(shown)); + lv_obj_set_style_text_color(widgets.value, lv_color_hex(live ? color : MUTED), 0); + set_text(widgets.detail, data.status_text()); + lv_obj_set_style_text_color(widgets.detail, lv_color_hex(color), 0); } inline void render_fleet_summary(lv_obj_t *label, const FleetCounts &counts) { diff --git a/esphome/bambu-display/generate.py b/esphome/bambu-display/generate.py index f3f02dc..717cbd8 100644 --- a/esphome/bambu-display/generate.py +++ b/esphome/bambu-display/generate.py @@ -245,48 +245,56 @@ def snapshot_lines(printer, expressions, trays): return lines -def overview_layout(count): - row_h = (436 - 46) // count - return row_h, row_h >= 52 +def overview_slots(count): + """Place one steam gauge per printer. Full-size arcs match the printer pages.""" + columns = 4 if count > 4 else max(count, 1) + rows = (count + columns - 1) // columns + top, bottom = 8, 400 + arc = min(154, (800 // columns) - 20, (bottom - top) // rows - 36) + col_w = 800 // columns + row_h = (bottom - top) // rows + slots = [] + for index in range(count): + row, col = divmod(index, columns) + in_row = min(columns, count - row * columns) + x0 = (800 - in_row * col_w) // 2 + gauge_w = arc + 16 + slots.append((x0 + col * col_w + (col_w - gauge_w) // 2, + top + row * row_h, arc)) + return slots -def overview_row(printer, y, row_h, two_line): +def overview_gauge(printer, x, y, arc): pid = printer['id'] - name_w, status_x, status_w = (430, 448, 312) if two_line else (240, 258, 150) - meta_x, meta_w, meta_y = (448, 312, 28) if two_line else (414, 346, 8) - children = [ - {'obj': {'id': f'{pid}_overview_accent', 'x': 0, 'y': 8, 'width': 4, - 'height': max(row_h - 16, 8), 'bg_color': MUTED, 'bg_opa': 'COVER', - 'border_width': 0, 'radius': 2, 'pad_all': 0, 'scrollable': False, - 'gesture_bubble': True}}, - label(printer['name'], 12, 2 if two_line else 6, name_w, 18 if two_line else 16), - label('SYNCING', status_x, 6 if two_line else 8, status_w, 16 if two_line else 14, - id=f'{pid}_overview_status', text_align='RIGHT', text_color=MUTED), + scale = arc / 154 + value_font = 32 if arc >= 140 else 18 + detail_font = 14 if arc >= 140 else 12 + name_font = 14 if arc >= 140 else 12 + open_page = [{'lvgl.page.show': f'{pid}_page'}] + inner = max(8, int(round(13 * scale))) + return [ + {'arc': {'id': f'{pid}_overview_arc', 'x': x + int(round(8 * scale)), 'y': y, + 'width': arc, 'height': arc, 'min_value': 0, 'max_value': 100, 'value': 0, + 'start_angle': 135, 'end_angle': 45, 'arc_width': inner, 'arc_color': TRACK, + 'indicator': {'arc_color': GREEN, 'arc_width': inner}, 'adjustable': False, + 'gesture_bubble': True, 'clickable': True, 'on_click': open_page}}, + label('--', x + int(round(14 * scale)), y + int(round(47 * scale)), int(round(142 * scale)), + value_font, id=f'{pid}_overview_value', text_align='CENTER', + clickable=True, on_click=open_page), + label('SYNCING', x + int(round(14 * scale)), y + int(round(86 * scale)), + int(round(142 * scale)), detail_font, id=f'{pid}_overview_detail', + text_align='CENTER', text_color=MUTED, clickable=True, on_click=open_page), + label(printer['name'], x, y + int(round(137 * scale)), arc + 16, name_font, + text_align='CENTER', text_color=GREEN, clickable=True, on_click=open_page), ] - if two_line: - children.append(label('Waiting for Home Assistant', 12, 28, 430, 12, - id=f'{pid}_overview_task', text_color=MUTED)) - children += [ - label('--', meta_x, meta_y, meta_w, 12 if two_line else 14, - id=f'{pid}_overview_meta', text_align='RIGHT', text_color=MUTED), - {'bar': {'id': f'{pid}_overview_progress', 'x': 12, 'y': row_h - 7, 'width': 748, 'height': 4, - 'min_value': 0, 'max_value': 100, 'value': 0, 'bg_color': TRACK, - 'indicator': {'bg_color': GREEN}, 'gesture_bubble': True}}, - ] - return {'obj': {'id': f'{pid}_overview_row', 'x': 8, 'y': y, 'width': 784, 'height': row_h, - 'bg_color': BG, 'bg_opa': 'COVER', 'border_width': 0, 'pad_all': 0, - 'radius': 0, 'scrollable': False, 'clickable': True, 'gesture_bubble': True, - 'on_click': [{'lvgl.page.show': f'{pid}_page'}], 'widgets': children}} def overview_page(printers): - row_h, two_line = overview_layout(len(printers)) - widgets = [ - label('FLEET', 16, 8, 180, 26, text_color=GREEN), - label('SYNCING', 210, 16, 574, 14, id='fleet_summary', text_align='RIGHT', text_color=MUTED), - ] - widgets += [overview_row(printer, 46 + index * row_h, row_h, two_line) - for index, printer in enumerate(printers)] + widgets = [] + for printer, (x, y, arc) in zip(printers, overview_slots(len(printers))): + widgets += overview_gauge(printer, x, y, arc) + widgets.append(label('SYNCING', 90, 404, 620, 14, id='fleet_summary', + text_align='CENTER', text_color=MUTED)) for direction, x, text in (('previous', 16, '<'), ('next', 720, '>')): widgets.append({'button': {'x': x, 'y': 446, 'width': 64, 'height': 30, 'bg_color': TRACK, 'radius': 8, 'border_width': 0, @@ -308,16 +316,14 @@ def overview_page(printers): def fleet_refresh_lines(snapshots): - _, two_line = overview_layout(len(snapshots)) lines = ['bambu_display::FleetCounts counts;'] for pid, snapshot in snapshots: - task = f'id({pid}_overview_task)' if two_line else 'nullptr' lines.append('{') lines.extend(snapshot) lines.append('bambu_display::tally(counts, data);') lines.append('bambu_display::render_overview(data, {' + ', '.join([ - f'id({pid}_overview_status)', task, f'id({pid}_overview_meta)', - f'id({pid}_overview_progress)', f'id({pid}_overview_accent)']) + '});') + f'id({pid}_overview_arc)', f'id({pid}_overview_value)', + f'id({pid}_overview_detail)']) + '});') lines.append('}') lines.append('bambu_display::render_fleet_summary(id(fleet_summary), counts);') return lines diff --git a/esphome/bambu-display/generated.yaml b/esphome/bambu-display/generated.yaml index 57ba348..2046f40 100644 --- a/esphome/bambu-display/generated.yaml +++ b/esphome/bambu-display/generated.yaml @@ -1533,7 +1533,7 @@ script: data.trays.push_back({"B3", id(prints_charming_tray_6_type).state, id(prints_charming_tray_6_color).state, id(prints_charming_tray_6_active).state}); data.trays.push_back({"B4", id(prints_charming_tray_7_type).state, id(prints_charming_tray_7_color).state, id(prints_charming_tray_7_active).state}); bambu_display::tally(counts, data); - bambu_display::render_overview(data, {id(prints_charming_overview_status), id(prints_charming_overview_task), id(prints_charming_overview_meta), id(prints_charming_overview_progress), id(prints_charming_overview_accent)}); + bambu_display::render_overview(data, {id(prints_charming_overview_arc), id(prints_charming_overview_value), id(prints_charming_overview_detail)}); } { bambu_display::Snapshot data; @@ -1568,7 +1568,7 @@ script: data.finish_time = esphome::ESPTime::from_epoch_local(clock.timestamp + seconds_left).strftime("%H:%M"); data.trays.push_back({"EXT", id(polly_pocket_tray_0_type).state, id(polly_pocket_tray_0_color).state, id(polly_pocket_tray_0_active).state}); bambu_display::tally(counts, data); - bambu_display::render_overview(data, {id(polly_pocket_overview_status), id(polly_pocket_overview_task), id(polly_pocket_overview_meta), id(polly_pocket_overview_progress), id(polly_pocket_overview_accent)}); + bambu_display::render_overview(data, {id(polly_pocket_overview_arc), id(polly_pocket_overview_value), id(polly_pocket_overview_detail)}); } { bambu_display::Snapshot data; @@ -1606,7 +1606,7 @@ script: data.trays.push_back({"A3", id(ds_turd_flinger_tray_2_type).state, id(ds_turd_flinger_tray_2_color).state, id(ds_turd_flinger_tray_2_active).state}); data.trays.push_back({"A4", id(ds_turd_flinger_tray_3_type).state, id(ds_turd_flinger_tray_3_color).state, id(ds_turd_flinger_tray_3_active).state}); bambu_display::tally(counts, data); - bambu_display::render_overview(data, {id(ds_turd_flinger_overview_status), id(ds_turd_flinger_overview_task), id(ds_turd_flinger_overview_meta), id(ds_turd_flinger_overview_progress), id(ds_turd_flinger_overview_accent)}); + bambu_display::render_overview(data, {id(ds_turd_flinger_overview_arc), id(ds_turd_flinger_overview_value), id(ds_turd_flinger_overview_detail)}); } { bambu_display::Snapshot data; @@ -1644,7 +1644,7 @@ script: data.trays.push_back({"A3", id(jordyns_layer_slayer_tray_2_type).state, id(jordyns_layer_slayer_tray_2_color).state, id(jordyns_layer_slayer_tray_2_active).state}); data.trays.push_back({"A4", id(jordyns_layer_slayer_tray_3_type).state, id(jordyns_layer_slayer_tray_3_color).state, id(jordyns_layer_slayer_tray_3_active).state}); bambu_display::tally(counts, data); - bambu_display::render_overview(data, {id(jordyns_layer_slayer_overview_status), id(jordyns_layer_slayer_overview_task), id(jordyns_layer_slayer_overview_meta), id(jordyns_layer_slayer_overview_progress), id(jordyns_layer_slayer_overview_accent)}); + bambu_display::render_overview(data, {id(jordyns_layer_slayer_overview_arc), id(jordyns_layer_slayer_overview_value), id(jordyns_layer_slayer_overview_detail)}); } { bambu_display::Snapshot data; @@ -1692,7 +1692,7 @@ script: data.trays.push_back({"B3", id(constipation_orion_tray_6_type).state, id(constipation_orion_tray_6_color).state, id(constipation_orion_tray_6_active).state}); data.trays.push_back({"B4", id(constipation_orion_tray_7_type).state, id(constipation_orion_tray_7_color).state, id(constipation_orion_tray_7_active).state}); bambu_display::tally(counts, data); - bambu_display::render_overview(data, {id(constipation_orion_overview_status), id(constipation_orion_overview_task), id(constipation_orion_overview_meta), id(constipation_orion_overview_progress), id(constipation_orion_overview_accent)}); + bambu_display::render_overview(data, {id(constipation_orion_overview_arc), id(constipation_orion_overview_value), id(constipation_orion_overview_detail)}); } { bambu_display::Snapshot data; @@ -1723,7 +1723,7 @@ script: if (clock.is_valid() && seconds_left >= 0) data.finish_time = esphome::ESPTime::from_epoch_local(clock.timestamp + seconds_left).strftime("%H:%M"); bambu_display::tally(counts, data); - bambu_display::render_overview(data, {id(centauri_carbon_overview_status), id(centauri_carbon_overview_task), id(centauri_carbon_overview_meta), id(centauri_carbon_overview_progress), id(centauri_carbon_overview_accent)}); + bambu_display::render_overview(data, {id(centauri_carbon_overview_arc), id(centauri_carbon_overview_value), id(centauri_carbon_overview_detail)}); } { bambu_display::Snapshot data; @@ -1750,7 +1750,7 @@ script: if (clock.is_valid() && seconds_left >= 0) data.finish_time = esphome::ESPTime::from_epoch_local(clock.timestamp + seconds_left).strftime("%H:%M"); bambu_display::tally(counts, data); - bambu_display::render_overview(data, {id(saturn_overview_status), id(saturn_overview_task), id(saturn_overview_meta), id(saturn_overview_progress), id(saturn_overview_accent)}); + bambu_display::render_overview(data, {id(saturn_overview_arc), id(saturn_overview_value), id(saturn_overview_detail)}); } bambu_display::render_fleet_summary(id(fleet_summary), counts); interval: @@ -1819,644 +1819,473 @@ lvgl: on_load: - script.execute: fleet_refresh widgets: - - label: - text: FLEET - x: 16 + - arc: + id: prints_charming_overview_arc + x: 23 y: 8 - width: 180 - height: 34 - text_color: 65280 - text_font: montserrat_26 + width: 154 + height: 154 + min_value: 0 + max_value: 100 + value: 0 + start_angle: 135 + end_angle: 45 + arc_width: 13 + arc_color: 1647403 + indicator: + arc_color: 65280 + arc_width: 13 + adjustable: false + gesture_bubble: true + clickable: true + on_click: + - lvgl.page.show: prints_charming_page + - label: + text: -- + x: 29 + y: 55 + width: 142 + height: 40 + text_color: 15658734 + text_font: montserrat_32 long_mode: DOT gesture_bubble: true + id: prints_charming_overview_value + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: prints_charming_page - label: text: SYNCING - x: 210 - y: 16 - width: 574 + x: 29 + y: 94 + width: 142 + height: 22 + text_color: 11449271 + text_font: montserrat_14 + long_mode: DOT + gesture_bubble: true + id: prints_charming_overview_detail + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: prints_charming_page + - label: + text: PrintsCharming + x: 15 + y: 145 + width: 170 + height: 22 + text_color: 65280 + text_font: montserrat_14 + long_mode: DOT + gesture_bubble: true + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: prints_charming_page + - arc: + id: polly_pocket_overview_arc + x: 223 + y: 8 + width: 154 + height: 154 + min_value: 0 + max_value: 100 + value: 0 + start_angle: 135 + end_angle: 45 + arc_width: 13 + arc_color: 1647403 + indicator: + arc_color: 65280 + arc_width: 13 + adjustable: false + gesture_bubble: true + clickable: true + on_click: + - lvgl.page.show: polly_pocket_page + - label: + text: -- + x: 229 + y: 55 + width: 142 + height: 40 + text_color: 15658734 + text_font: montserrat_32 + long_mode: DOT + gesture_bubble: true + id: polly_pocket_overview_value + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: polly_pocket_page + - label: + text: SYNCING + x: 229 + y: 94 + width: 142 + height: 22 + text_color: 11449271 + text_font: montserrat_14 + long_mode: DOT + gesture_bubble: true + id: polly_pocket_overview_detail + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: polly_pocket_page + - label: + text: Polly-Pocket + x: 215 + y: 145 + width: 170 + height: 22 + text_color: 65280 + text_font: montserrat_14 + long_mode: DOT + gesture_bubble: true + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: polly_pocket_page + - arc: + id: ds_turd_flinger_overview_arc + x: 423 + y: 8 + width: 154 + height: 154 + min_value: 0 + max_value: 100 + value: 0 + start_angle: 135 + end_angle: 45 + arc_width: 13 + arc_color: 1647403 + indicator: + arc_color: 65280 + arc_width: 13 + adjustable: false + gesture_bubble: true + clickable: true + on_click: + - lvgl.page.show: ds_turd_flinger_page + - label: + text: -- + x: 429 + y: 55 + width: 142 + height: 40 + text_color: 15658734 + text_font: montserrat_32 + long_mode: DOT + gesture_bubble: true + id: ds_turd_flinger_overview_value + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: ds_turd_flinger_page + - label: + text: SYNCING + x: 429 + y: 94 + width: 142 + height: 22 + text_color: 11449271 + text_font: montserrat_14 + long_mode: DOT + gesture_bubble: true + id: ds_turd_flinger_overview_detail + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: ds_turd_flinger_page + - label: + text: Ds-Turd-Flinger + x: 415 + y: 145 + width: 170 + height: 22 + text_color: 65280 + text_font: montserrat_14 + long_mode: DOT + gesture_bubble: true + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: ds_turd_flinger_page + - arc: + id: jordyns_layer_slayer_overview_arc + x: 623 + y: 8 + width: 154 + height: 154 + min_value: 0 + max_value: 100 + value: 0 + start_angle: 135 + end_angle: 45 + arc_width: 13 + arc_color: 1647403 + indicator: + arc_color: 65280 + arc_width: 13 + adjustable: false + gesture_bubble: true + clickable: true + on_click: + - lvgl.page.show: jordyns_layer_slayer_page + - label: + text: -- + x: 629 + y: 55 + width: 142 + height: 40 + text_color: 15658734 + text_font: montserrat_32 + long_mode: DOT + gesture_bubble: true + id: jordyns_layer_slayer_overview_value + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: jordyns_layer_slayer_page + - label: + text: SYNCING + x: 629 + y: 94 + width: 142 + height: 22 + text_color: 11449271 + text_font: montserrat_14 + long_mode: DOT + gesture_bubble: true + id: jordyns_layer_slayer_overview_detail + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: jordyns_layer_slayer_page + - label: + text: Jordyns-Layer-Slayer + x: 615 + y: 145 + width: 170 + height: 22 + text_color: 65280 + text_font: montserrat_14 + long_mode: DOT + gesture_bubble: true + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: jordyns_layer_slayer_page + - arc: + id: constipation_orion_overview_arc + x: 123 + y: 204 + width: 154 + height: 154 + min_value: 0 + max_value: 100 + value: 0 + start_angle: 135 + end_angle: 45 + arc_width: 13 + arc_color: 1647403 + indicator: + arc_color: 65280 + arc_width: 13 + adjustable: false + gesture_bubble: true + clickable: true + on_click: + - lvgl.page.show: constipation_orion_page + - label: + text: -- + x: 129 + y: 251 + width: 142 + height: 40 + text_color: 15658734 + text_font: montserrat_32 + long_mode: DOT + gesture_bubble: true + id: constipation_orion_overview_value + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: constipation_orion_page + - label: + text: SYNCING + x: 129 + y: 290 + width: 142 + height: 22 + text_color: 11449271 + text_font: montserrat_14 + long_mode: DOT + gesture_bubble: true + id: constipation_orion_overview_detail + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: constipation_orion_page + - label: + text: Constipation-Orion + x: 115 + y: 341 + width: 170 + height: 22 + text_color: 65280 + text_font: montserrat_14 + long_mode: DOT + gesture_bubble: true + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: constipation_orion_page + - arc: + id: centauri_carbon_overview_arc + x: 323 + y: 204 + width: 154 + height: 154 + min_value: 0 + max_value: 100 + value: 0 + start_angle: 135 + end_angle: 45 + arc_width: 13 + arc_color: 1647403 + indicator: + arc_color: 65280 + arc_width: 13 + adjustable: false + gesture_bubble: true + clickable: true + on_click: + - lvgl.page.show: centauri_carbon_page + - label: + text: -- + x: 329 + y: 251 + width: 142 + height: 40 + text_color: 15658734 + text_font: montserrat_32 + long_mode: DOT + gesture_bubble: true + id: centauri_carbon_overview_value + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: centauri_carbon_page + - label: + text: SYNCING + x: 329 + y: 290 + width: 142 + height: 22 + text_color: 11449271 + text_font: montserrat_14 + long_mode: DOT + gesture_bubble: true + id: centauri_carbon_overview_detail + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: centauri_carbon_page + - label: + text: Centauri Carbon + x: 315 + y: 341 + width: 170 + height: 22 + text_color: 65280 + text_font: montserrat_14 + long_mode: DOT + gesture_bubble: true + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: centauri_carbon_page + - arc: + id: saturn_overview_arc + x: 523 + y: 204 + width: 154 + height: 154 + min_value: 0 + max_value: 100 + value: 0 + start_angle: 135 + end_angle: 45 + arc_width: 13 + arc_color: 1647403 + indicator: + arc_color: 65280 + arc_width: 13 + adjustable: false + gesture_bubble: true + clickable: true + on_click: + - lvgl.page.show: saturn_page + - label: + text: -- + x: 529 + y: 251 + width: 142 + height: 40 + text_color: 15658734 + text_font: montserrat_32 + long_mode: DOT + gesture_bubble: true + id: saturn_overview_value + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: saturn_page + - label: + text: SYNCING + x: 529 + y: 290 + width: 142 + height: 22 + text_color: 11449271 + text_font: montserrat_14 + long_mode: DOT + gesture_bubble: true + id: saturn_overview_detail + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: saturn_page + - label: + text: Saturn + x: 515 + y: 341 + width: 170 + height: 22 + text_color: 65280 + text_font: montserrat_14 + long_mode: DOT + gesture_bubble: true + text_align: CENTER + clickable: true + on_click: + - lvgl.page.show: saturn_page + - label: + text: SYNCING + x: 90 + y: 404 + width: 620 height: 22 text_color: 11449271 text_font: montserrat_14 long_mode: DOT gesture_bubble: true id: fleet_summary - text_align: RIGHT - - obj: - id: prints_charming_overview_row - x: 8 - y: 46 - width: 784 - height: 55 - bg_color: 0 - bg_opa: COVER - border_width: 0 - pad_all: 0 - radius: 0 - scrollable: false - clickable: true - gesture_bubble: true - on_click: - - lvgl.page.show: prints_charming_page - widgets: - - obj: - id: prints_charming_overview_accent - x: 0 - y: 8 - width: 4 - height: 39 - bg_color: 11449271 - bg_opa: COVER - border_width: 0 - radius: 2 - pad_all: 0 - scrollable: false - gesture_bubble: true - - label: - text: PrintsCharming - x: 12 - y: 2 - width: 430 - height: 26 - text_color: 15658734 - text_font: montserrat_18 - long_mode: DOT - gesture_bubble: true - - label: - text: SYNCING - x: 448 - y: 6 - width: 312 - height: 24 - text_color: 11449271 - text_font: montserrat_16 - long_mode: DOT - gesture_bubble: true - id: prints_charming_overview_status - text_align: RIGHT - - label: - text: Waiting for Home Assistant - x: 12 - y: 28 - width: 430 - height: 20 - text_color: 11449271 - text_font: montserrat_12 - long_mode: DOT - gesture_bubble: true - id: prints_charming_overview_task - - label: - text: -- - x: 448 - y: 28 - width: 312 - height: 20 - text_color: 11449271 - text_font: montserrat_12 - long_mode: DOT - gesture_bubble: true - id: prints_charming_overview_meta - text_align: RIGHT - - bar: - id: prints_charming_overview_progress - x: 12 - y: 48 - width: 748 - height: 4 - min_value: 0 - max_value: 100 - value: 0 - bg_color: 1647403 - indicator: - bg_color: 65280 - gesture_bubble: true - - obj: - id: polly_pocket_overview_row - x: 8 - y: 101 - width: 784 - height: 55 - bg_color: 0 - bg_opa: COVER - border_width: 0 - pad_all: 0 - radius: 0 - scrollable: false - clickable: true - gesture_bubble: true - on_click: - - lvgl.page.show: polly_pocket_page - widgets: - - obj: - id: polly_pocket_overview_accent - x: 0 - y: 8 - width: 4 - height: 39 - bg_color: 11449271 - bg_opa: COVER - border_width: 0 - radius: 2 - pad_all: 0 - scrollable: false - gesture_bubble: true - - label: - text: Polly-Pocket - x: 12 - y: 2 - width: 430 - height: 26 - text_color: 15658734 - text_font: montserrat_18 - long_mode: DOT - gesture_bubble: true - - label: - text: SYNCING - x: 448 - y: 6 - width: 312 - height: 24 - text_color: 11449271 - text_font: montserrat_16 - long_mode: DOT - gesture_bubble: true - id: polly_pocket_overview_status - text_align: RIGHT - - label: - text: Waiting for Home Assistant - x: 12 - y: 28 - width: 430 - height: 20 - text_color: 11449271 - text_font: montserrat_12 - long_mode: DOT - gesture_bubble: true - id: polly_pocket_overview_task - - label: - text: -- - x: 448 - y: 28 - width: 312 - height: 20 - text_color: 11449271 - text_font: montserrat_12 - long_mode: DOT - gesture_bubble: true - id: polly_pocket_overview_meta - text_align: RIGHT - - bar: - id: polly_pocket_overview_progress - x: 12 - y: 48 - width: 748 - height: 4 - min_value: 0 - max_value: 100 - value: 0 - bg_color: 1647403 - indicator: - bg_color: 65280 - gesture_bubble: true - - obj: - id: ds_turd_flinger_overview_row - x: 8 - y: 156 - width: 784 - height: 55 - bg_color: 0 - bg_opa: COVER - border_width: 0 - pad_all: 0 - radius: 0 - scrollable: false - clickable: true - gesture_bubble: true - on_click: - - lvgl.page.show: ds_turd_flinger_page - widgets: - - obj: - id: ds_turd_flinger_overview_accent - x: 0 - y: 8 - width: 4 - height: 39 - bg_color: 11449271 - bg_opa: COVER - border_width: 0 - radius: 2 - pad_all: 0 - scrollable: false - gesture_bubble: true - - label: - text: Ds-Turd-Flinger - x: 12 - y: 2 - width: 430 - height: 26 - text_color: 15658734 - text_font: montserrat_18 - long_mode: DOT - gesture_bubble: true - - label: - text: SYNCING - x: 448 - y: 6 - width: 312 - height: 24 - text_color: 11449271 - text_font: montserrat_16 - long_mode: DOT - gesture_bubble: true - id: ds_turd_flinger_overview_status - text_align: RIGHT - - label: - text: Waiting for Home Assistant - x: 12 - y: 28 - width: 430 - height: 20 - text_color: 11449271 - text_font: montserrat_12 - long_mode: DOT - gesture_bubble: true - id: ds_turd_flinger_overview_task - - label: - text: -- - x: 448 - y: 28 - width: 312 - height: 20 - text_color: 11449271 - text_font: montserrat_12 - long_mode: DOT - gesture_bubble: true - id: ds_turd_flinger_overview_meta - text_align: RIGHT - - bar: - id: ds_turd_flinger_overview_progress - x: 12 - y: 48 - width: 748 - height: 4 - min_value: 0 - max_value: 100 - value: 0 - bg_color: 1647403 - indicator: - bg_color: 65280 - gesture_bubble: true - - obj: - id: jordyns_layer_slayer_overview_row - x: 8 - y: 211 - width: 784 - height: 55 - bg_color: 0 - bg_opa: COVER - border_width: 0 - pad_all: 0 - radius: 0 - scrollable: false - clickable: true - gesture_bubble: true - on_click: - - lvgl.page.show: jordyns_layer_slayer_page - widgets: - - obj: - id: jordyns_layer_slayer_overview_accent - x: 0 - y: 8 - width: 4 - height: 39 - bg_color: 11449271 - bg_opa: COVER - border_width: 0 - radius: 2 - pad_all: 0 - scrollable: false - gesture_bubble: true - - label: - text: Jordyns-Layer-Slayer - x: 12 - y: 2 - width: 430 - height: 26 - text_color: 15658734 - text_font: montserrat_18 - long_mode: DOT - gesture_bubble: true - - label: - text: SYNCING - x: 448 - y: 6 - width: 312 - height: 24 - text_color: 11449271 - text_font: montserrat_16 - long_mode: DOT - gesture_bubble: true - id: jordyns_layer_slayer_overview_status - text_align: RIGHT - - label: - text: Waiting for Home Assistant - x: 12 - y: 28 - width: 430 - height: 20 - text_color: 11449271 - text_font: montserrat_12 - long_mode: DOT - gesture_bubble: true - id: jordyns_layer_slayer_overview_task - - label: - text: -- - x: 448 - y: 28 - width: 312 - height: 20 - text_color: 11449271 - text_font: montserrat_12 - long_mode: DOT - gesture_bubble: true - id: jordyns_layer_slayer_overview_meta - text_align: RIGHT - - bar: - id: jordyns_layer_slayer_overview_progress - x: 12 - y: 48 - width: 748 - height: 4 - min_value: 0 - max_value: 100 - value: 0 - bg_color: 1647403 - indicator: - bg_color: 65280 - gesture_bubble: true - - obj: - id: constipation_orion_overview_row - x: 8 - y: 266 - width: 784 - height: 55 - bg_color: 0 - bg_opa: COVER - border_width: 0 - pad_all: 0 - radius: 0 - scrollable: false - clickable: true - gesture_bubble: true - on_click: - - lvgl.page.show: constipation_orion_page - widgets: - - obj: - id: constipation_orion_overview_accent - x: 0 - y: 8 - width: 4 - height: 39 - bg_color: 11449271 - bg_opa: COVER - border_width: 0 - radius: 2 - pad_all: 0 - scrollable: false - gesture_bubble: true - - label: - text: Constipation-Orion - x: 12 - y: 2 - width: 430 - height: 26 - text_color: 15658734 - text_font: montserrat_18 - long_mode: DOT - gesture_bubble: true - - label: - text: SYNCING - x: 448 - y: 6 - width: 312 - height: 24 - text_color: 11449271 - text_font: montserrat_16 - long_mode: DOT - gesture_bubble: true - id: constipation_orion_overview_status - text_align: RIGHT - - label: - text: Waiting for Home Assistant - x: 12 - y: 28 - width: 430 - height: 20 - text_color: 11449271 - text_font: montserrat_12 - long_mode: DOT - gesture_bubble: true - id: constipation_orion_overview_task - - label: - text: -- - x: 448 - y: 28 - width: 312 - height: 20 - text_color: 11449271 - text_font: montserrat_12 - long_mode: DOT - gesture_bubble: true - id: constipation_orion_overview_meta - text_align: RIGHT - - bar: - id: constipation_orion_overview_progress - x: 12 - y: 48 - width: 748 - height: 4 - min_value: 0 - max_value: 100 - value: 0 - bg_color: 1647403 - indicator: - bg_color: 65280 - gesture_bubble: true - - obj: - id: centauri_carbon_overview_row - x: 8 - y: 321 - width: 784 - height: 55 - bg_color: 0 - bg_opa: COVER - border_width: 0 - pad_all: 0 - radius: 0 - scrollable: false - clickable: true - gesture_bubble: true - on_click: - - lvgl.page.show: centauri_carbon_page - widgets: - - obj: - id: centauri_carbon_overview_accent - x: 0 - y: 8 - width: 4 - height: 39 - bg_color: 11449271 - bg_opa: COVER - border_width: 0 - radius: 2 - pad_all: 0 - scrollable: false - gesture_bubble: true - - label: - text: Centauri Carbon - x: 12 - y: 2 - width: 430 - height: 26 - text_color: 15658734 - text_font: montserrat_18 - long_mode: DOT - gesture_bubble: true - - label: - text: SYNCING - x: 448 - y: 6 - width: 312 - height: 24 - text_color: 11449271 - text_font: montserrat_16 - long_mode: DOT - gesture_bubble: true - id: centauri_carbon_overview_status - text_align: RIGHT - - label: - text: Waiting for Home Assistant - x: 12 - y: 28 - width: 430 - height: 20 - text_color: 11449271 - text_font: montserrat_12 - long_mode: DOT - gesture_bubble: true - id: centauri_carbon_overview_task - - label: - text: -- - x: 448 - y: 28 - width: 312 - height: 20 - text_color: 11449271 - text_font: montserrat_12 - long_mode: DOT - gesture_bubble: true - id: centauri_carbon_overview_meta - text_align: RIGHT - - bar: - id: centauri_carbon_overview_progress - x: 12 - y: 48 - width: 748 - height: 4 - min_value: 0 - max_value: 100 - value: 0 - bg_color: 1647403 - indicator: - bg_color: 65280 - gesture_bubble: true - - obj: - id: saturn_overview_row - x: 8 - y: 376 - width: 784 - height: 55 - bg_color: 0 - bg_opa: COVER - border_width: 0 - pad_all: 0 - radius: 0 - scrollable: false - clickable: true - gesture_bubble: true - on_click: - - lvgl.page.show: saturn_page - widgets: - - obj: - id: saturn_overview_accent - x: 0 - y: 8 - width: 4 - height: 39 - bg_color: 11449271 - bg_opa: COVER - border_width: 0 - radius: 2 - pad_all: 0 - scrollable: false - gesture_bubble: true - - label: - text: Saturn - x: 12 - y: 2 - width: 430 - height: 26 - text_color: 15658734 - text_font: montserrat_18 - long_mode: DOT - gesture_bubble: true - - label: - text: SYNCING - x: 448 - y: 6 - width: 312 - height: 24 - text_color: 11449271 - text_font: montserrat_16 - long_mode: DOT - gesture_bubble: true - id: saturn_overview_status - text_align: RIGHT - - label: - text: Waiting for Home Assistant - x: 12 - y: 28 - width: 430 - height: 20 - text_color: 11449271 - text_font: montserrat_12 - long_mode: DOT - gesture_bubble: true - id: saturn_overview_task - - label: - text: -- - x: 448 - y: 28 - width: 312 - height: 20 - text_color: 11449271 - text_font: montserrat_12 - long_mode: DOT - gesture_bubble: true - id: saturn_overview_meta - text_align: RIGHT - - bar: - id: saturn_overview_progress - x: 12 - y: 48 - width: 748 - height: 4 - min_value: 0 - max_value: 100 - value: 0 - bg_color: 1647403 - indicator: - bg_color: 65280 - gesture_bubble: true + text_align: CENTER - button: x: 16 y: 446 diff --git a/esphome/bambu-display/preview.py b/esphome/bambu-display/preview.py index 9e7be8a..72a417d 100644 --- a/esphome/bambu-display/preview.py +++ b/esphome/bambu-display/preview.py @@ -98,7 +98,7 @@ inline void setup() { for printer in printers: pid = printer['id'] steps.append({'lambda': Lambda( - f'if (std::string(lv_label_get_text(id({pid}_overview_status))) != "PRINTING") ' + f'if (std::string(lv_label_get_text(id({pid}_overview_detail))) != "PRINTING") ' f'{{ ESP_LOGE("preview", "Fleet row render failed: {pid}"); exit(7); }}')}) for printer in printers: steps += [{'lvgl.page.show': printer['id'] + '_page'}, diff --git a/esphome/bambu-display/test_generate.py b/esphome/bambu-display/test_generate.py index fb8d38d..60dbd65 100644 --- a/esphome/bambu-display/test_generate.py +++ b/esphome/bambu-display/test_generate.py @@ -152,19 +152,19 @@ class FleetTests(unittest.TestCase): page = package['lvgl']['pages'][0] self.assertEqual(page['id'], 'fleet_page') self.assertFalse(page.get('skip')) - rows = [widget['obj'] for widget in page['widgets'] - if 'obj' in widget and str(widget['obj'].get('id', '')).endswith('_overview_row')] - self.assertEqual([row['id'][:-len('_overview_row')] for row in rows], + arcs = [widget['arc'] for widget in page['widgets'] if 'arc' in widget] + self.assertEqual([arc['id'][:-len('_overview_arc')] for arc in arcs], [printer['id'] for printer in self.printers]) - previous = 46 - for row, printer in zip(rows, self.printers): - self.assertEqual(row['on_click'], [{'lvgl.page.show': printer['id'] + '_page'}]) - self.assertGreaterEqual(row['y'], previous) - self.assertLessEqual(row['y'] + row['height'], 440) - previous = row['y'] + row['height'] + for arc, printer in zip(arcs, self.printers): + self.assertEqual(arc['on_click'], [{'lvgl.page.show': printer['id'] + '_page'}]) + self.assertEqual(arc['width'], 154) + self.assertEqual(arc['start_angle'], 135) + self.assertLessEqual(arc['x'] + arc['width'], 800) + self.assertLessEqual(arc['y'] + arc['height'], 480) code = next(script['then'][0]['lambda'] for script in package['script'] if script['id'] == 'fleet_refresh') for printer in self.printers: - self.assertIn(f'id({printer["id"]}_overview_status)', code) + self.assertIn(f'id({printer["id"]}_overview_arc)', code) + self.assertIn(f'id({printer["id"]}_overview_detail)', code) shown = [step['if']['condition']['lvgl.page.is_showing'] for step in package['interval'][0]['then']] self.assertIn('fleet_page', shown) charming = next(item for item in package['lvgl']['pages'] if item['id'] == 'prints_charming_page')