Show a grey ring behind black filament gauges so they stay visible.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Joshua King
2026-10-20 20:46:41 -04:00
co-authored by Cursor
parent 5b8a3f24c2
commit 12f8ba2def
6 changed files with 208 additions and 11 deletions
+2 -1
View File
@@ -18,7 +18,8 @@ Home Assistant must be running and its printer integrations must have current pr
The configured swipe order is:
1. Print Status Overview — one steam gauge per printer. Each ring uses the
color of that printer's active filament. The center shows the percent
color of that printer's active filament. Black filament keeps a grey ring
behind the gauge so it stays visible on the black page. The center shows the percent
complete in white, with the printer name and estimated finish time underneath.
The local time is in the top-right corner. Tap a gauge to open that printer.
2. PrintsCharming — P1S, Office, both four-slot AMS units.
+14 -1
View File
@@ -17,6 +17,7 @@ constexpr uint32_t MUTED = 0xAEB3B7;
constexpr uint32_t RED = 0xFF737B;
constexpr uint32_t AMBER = 0xF2C572;
constexpr uint32_t TRACK = 0x19232B;
constexpr uint32_t RING_BACKING = 0x6E767E;
inline std::string lower(std::string text) {
for (char &c : text) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
@@ -104,6 +105,11 @@ inline uint32_t filament_color(std::string value) {
return rgb;
}
// A black ring disappears on the black page. Anything this dark needs a grey arc behind it.
inline bool filament_is_black(uint32_t color) {
return ((color >> 16) & 0xFF) <= 0x28 && ((color >> 8) & 0xFF) <= 0x28 && (color & 0xFF) <= 0x28;
}
struct Tray {
std::string label, type, color, active;
};
@@ -299,7 +305,7 @@ struct Widgets {
lv_obj_t *elapsed = nullptr;
};
struct OverviewWidgets {
lv_obj_t *arc, *value, *finish;
lv_obj_t *arc, *value, *finish, *backing;
};
inline void set_text(lv_obj_t *label, const std::string &text) {
@@ -376,6 +382,13 @@ inline void render_overview(const Snapshot &data, const OverviewWidgets &widgets
lv_obj_set_style_arc_color(widgets.arc, lv_color_hex(filament), LV_PART_MAIN);
lv_obj_set_style_arc_color(widgets.arc, lv_color_hex(filament), LV_PART_INDICATOR);
lv_arc_set_value(widgets.arc, 100);
if (filament_is_black(filament)) {
lv_obj_set_style_arc_color(widgets.backing, lv_color_hex(RING_BACKING), LV_PART_MAIN);
lv_obj_set_style_arc_color(widgets.backing, lv_color_hex(RING_BACKING), LV_PART_INDICATOR);
lv_obj_clear_flag(widgets.backing, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_add_flag(widgets.backing, LV_OBJ_FLAG_HIDDEN);
}
set_text(widgets.value, number(shown));
lv_obj_set_style_text_color(widgets.value, lv_color_hex(0xFFFFFF), 0);
set_text(widgets.finish, live && !data.finish_time.empty() ? data.finish_time : "--");
+10 -1
View File
@@ -32,6 +32,7 @@ ELEGOO_FDM = {'nozzle': 'nozzle_temperature', 'bed': 'bed_temperature',
ELEGOO_RESIN = {'uv': 'uv_led_temp', 'elapsed': 'current_print_time'}
BG, CARD, TRACK, TEXT_COLOR, MUTED, GREEN = (
0x000000, 0x000000, 0x19232B, 0xEEEEEE, 0xAEB3B7, 0x00FF00)
RING_BACKING = 0x6E767E
ORANGE, CYAN = 0xFFAA00, 0x00E5E5
@@ -272,7 +273,15 @@ def overview_gauge(printer, x, y, arc):
inner = max(10, int(round(13 * scale)))
inner_x = x + int(round(8 * scale))
number_y = y + int(round(arc * 0.30))
pad = 8
return [
{'arc': {'id': f'{pid}_overview_ring', 'x': inner_x - pad // 2, 'y': y - pad // 2,
'width': arc + pad, 'height': arc + pad, 'min_value': 0, 'max_value': 100,
'value': 100, 'start_angle': 135, 'end_angle': 45,
'arc_width': inner + pad, 'arc_color': RING_BACKING,
'indicator': {'arc_color': RING_BACKING, 'arc_width': inner + pad},
'adjustable': False, 'hidden': True, 'gesture_bubble': True, 'clickable': True,
'on_click': open_page}},
{'arc': {'id': f'{pid}_overview_arc', 'x': inner_x, 'y': y,
'width': arc, 'height': arc, 'min_value': 0, 'max_value': 100, 'value': 100,
'start_angle': 135, 'end_angle': 45, 'arc_width': inner, 'arc_color': GREEN,
@@ -324,7 +333,7 @@ def fleet_refresh_lines(snapshots):
lines.extend(snapshot)
lines.append('bambu_display::render_overview(data, {' + ', '.join([
f'id({pid}_overview_arc)', f'id({pid}_overview_value)',
f'id({pid}_overview_finish)']) + '});')
f'id({pid}_overview_finish)', f'id({pid}_overview_ring)']) + '});')
lines.append('}')
lines += [
'{',
+161 -7
View File
@@ -1531,7 +1531,7 @@ script:
data.trays.push_back({"B2", id(prints_charming_tray_5_type).state, id(prints_charming_tray_5_color).state, id(prints_charming_tray_5_active).state});
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::render_overview(data, {id(prints_charming_overview_arc), id(prints_charming_overview_value), id(prints_charming_overview_finish)});
bambu_display::render_overview(data, {id(prints_charming_overview_arc), id(prints_charming_overview_value), id(prints_charming_overview_finish), id(prints_charming_overview_ring)});
}
{
bambu_display::Snapshot data;
@@ -1565,7 +1565,7 @@ script:
if (clock.is_valid() && seconds_left >= 0)
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::render_overview(data, {id(polly_pocket_overview_arc), id(polly_pocket_overview_value), id(polly_pocket_overview_finish)});
bambu_display::render_overview(data, {id(polly_pocket_overview_arc), id(polly_pocket_overview_value), id(polly_pocket_overview_finish), id(polly_pocket_overview_ring)});
}
{
bambu_display::Snapshot data;
@@ -1602,7 +1602,7 @@ script:
data.trays.push_back({"A2", id(ds_turd_flinger_tray_1_type).state, id(ds_turd_flinger_tray_1_color).state, id(ds_turd_flinger_tray_1_active).state});
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::render_overview(data, {id(ds_turd_flinger_overview_arc), id(ds_turd_flinger_overview_value), id(ds_turd_flinger_overview_finish)});
bambu_display::render_overview(data, {id(ds_turd_flinger_overview_arc), id(ds_turd_flinger_overview_value), id(ds_turd_flinger_overview_finish), id(ds_turd_flinger_overview_ring)});
}
{
bambu_display::Snapshot data;
@@ -1639,7 +1639,7 @@ script:
data.trays.push_back({"A2", id(jordyns_layer_slayer_tray_1_type).state, id(jordyns_layer_slayer_tray_1_color).state, id(jordyns_layer_slayer_tray_1_active).state});
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::render_overview(data, {id(jordyns_layer_slayer_overview_arc), id(jordyns_layer_slayer_overview_value), id(jordyns_layer_slayer_overview_finish)});
bambu_display::render_overview(data, {id(jordyns_layer_slayer_overview_arc), id(jordyns_layer_slayer_overview_value), id(jordyns_layer_slayer_overview_finish), id(jordyns_layer_slayer_overview_ring)});
}
{
bambu_display::Snapshot data;
@@ -1686,7 +1686,7 @@ script:
data.trays.push_back({"B2", id(constipation_orion_tray_5_type).state, id(constipation_orion_tray_5_color).state, id(constipation_orion_tray_5_active).state});
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::render_overview(data, {id(constipation_orion_overview_arc), id(constipation_orion_overview_value), id(constipation_orion_overview_finish)});
bambu_display::render_overview(data, {id(constipation_orion_overview_arc), id(constipation_orion_overview_value), id(constipation_orion_overview_finish), id(constipation_orion_overview_ring)});
}
{
bambu_display::Snapshot data;
@@ -1716,7 +1716,7 @@ script:
const auto clock = id(ha_time).now();
if (clock.is_valid() && seconds_left >= 0)
data.finish_time = esphome::ESPTime::from_epoch_local(clock.timestamp + seconds_left).strftime("%H:%M");
bambu_display::render_overview(data, {id(centauri_carbon_overview_arc), id(centauri_carbon_overview_value), id(centauri_carbon_overview_finish)});
bambu_display::render_overview(data, {id(centauri_carbon_overview_arc), id(centauri_carbon_overview_value), id(centauri_carbon_overview_finish), id(centauri_carbon_overview_ring)});
}
{
bambu_display::Snapshot data;
@@ -1742,7 +1742,7 @@ script:
const auto clock = id(ha_time).now();
if (clock.is_valid() && seconds_left >= 0)
data.finish_time = esphome::ESPTime::from_epoch_local(clock.timestamp + seconds_left).strftime("%H:%M");
bambu_display::render_overview(data, {id(saturn_overview_arc), id(saturn_overview_value), id(saturn_overview_finish)});
bambu_display::render_overview(data, {id(saturn_overview_arc), id(saturn_overview_value), id(saturn_overview_finish), id(saturn_overview_ring)});
}
{
const auto clock = id(ha_time).now();
@@ -1841,6 +1841,28 @@ lvgl:
gesture_bubble: true
id: fleet_clock
text_align: RIGHT
- arc:
id: prints_charming_overview_ring
x: 23
y: 42
width: 153
height: 153
min_value: 0
max_value: 100
value: 100
start_angle: 135
end_angle: 45
arc_width: 20
arc_color: 7239294
indicator:
arc_color: 7239294
arc_width: 20
adjustable: false
hidden: true
gesture_bubble: true
clickable: true
on_click:
- lvgl.page.show: prints_charming_page
- arc:
id: prints_charming_overview_arc
x: 27
@@ -1921,6 +1943,28 @@ lvgl:
clickable: true
on_click:
- lvgl.page.show: prints_charming_page
- arc:
id: polly_pocket_overview_ring
x: 223
y: 42
width: 153
height: 153
min_value: 0
max_value: 100
value: 100
start_angle: 135
end_angle: 45
arc_width: 20
arc_color: 7239294
indicator:
arc_color: 7239294
arc_width: 20
adjustable: false
hidden: true
gesture_bubble: true
clickable: true
on_click:
- lvgl.page.show: polly_pocket_page
- arc:
id: polly_pocket_overview_arc
x: 227
@@ -2001,6 +2045,28 @@ lvgl:
clickable: true
on_click:
- lvgl.page.show: polly_pocket_page
- arc:
id: ds_turd_flinger_overview_ring
x: 423
y: 42
width: 153
height: 153
min_value: 0
max_value: 100
value: 100
start_angle: 135
end_angle: 45
arc_width: 20
arc_color: 7239294
indicator:
arc_color: 7239294
arc_width: 20
adjustable: false
hidden: true
gesture_bubble: true
clickable: true
on_click:
- lvgl.page.show: ds_turd_flinger_page
- arc:
id: ds_turd_flinger_overview_arc
x: 427
@@ -2081,6 +2147,28 @@ lvgl:
clickable: true
on_click:
- lvgl.page.show: ds_turd_flinger_page
- arc:
id: jordyns_layer_slayer_overview_ring
x: 623
y: 42
width: 153
height: 153
min_value: 0
max_value: 100
value: 100
start_angle: 135
end_angle: 45
arc_width: 20
arc_color: 7239294
indicator:
arc_color: 7239294
arc_width: 20
adjustable: false
hidden: true
gesture_bubble: true
clickable: true
on_click:
- lvgl.page.show: jordyns_layer_slayer_page
- arc:
id: jordyns_layer_slayer_overview_arc
x: 627
@@ -2161,6 +2249,28 @@ lvgl:
clickable: true
on_click:
- lvgl.page.show: jordyns_layer_slayer_page
- arc:
id: constipation_orion_overview_ring
x: 123
y: 235
width: 153
height: 153
min_value: 0
max_value: 100
value: 100
start_angle: 135
end_angle: 45
arc_width: 20
arc_color: 7239294
indicator:
arc_color: 7239294
arc_width: 20
adjustable: false
hidden: true
gesture_bubble: true
clickable: true
on_click:
- lvgl.page.show: constipation_orion_page
- arc:
id: constipation_orion_overview_arc
x: 127
@@ -2241,6 +2351,28 @@ lvgl:
clickable: true
on_click:
- lvgl.page.show: constipation_orion_page
- arc:
id: centauri_carbon_overview_ring
x: 323
y: 235
width: 153
height: 153
min_value: 0
max_value: 100
value: 100
start_angle: 135
end_angle: 45
arc_width: 20
arc_color: 7239294
indicator:
arc_color: 7239294
arc_width: 20
adjustable: false
hidden: true
gesture_bubble: true
clickable: true
on_click:
- lvgl.page.show: centauri_carbon_page
- arc:
id: centauri_carbon_overview_arc
x: 327
@@ -2321,6 +2453,28 @@ lvgl:
clickable: true
on_click:
- lvgl.page.show: centauri_carbon_page
- arc:
id: saturn_overview_ring
x: 523
y: 235
width: 153
height: 153
min_value: 0
max_value: 100
value: 100
start_angle: 135
end_angle: 45
arc_width: 20
arc_color: 7239294
indicator:
arc_color: 7239294
arc_width: 20
adjustable: false
hidden: true
gesture_bubble: true
clickable: true
on_click:
- lvgl.page.show: saturn_page
- arc:
id: saturn_overview_arc
x: 527
+6
View File
@@ -130,6 +130,12 @@ int main() {
assert(data.active_filament_color() == 0xFF8800);
data.trays.clear();
assert(data.active_filament_color() == GREEN);
assert(filament_is_black(0x000000));
assert(filament_is_black(filament_color("#000000FF")));
assert(filament_is_black(0x202020));
assert(!filament_is_black(0xFFFFFF));
assert(!filament_is_black(GREEN));
assert(!filament_is_black(0xFF8800));
FleetCounts counts;
data.connected = true;
+15 -1
View File
@@ -152,9 +152,22 @@ class FleetTests(unittest.TestCase):
page = package['lvgl']['pages'][0]
self.assertEqual(page['id'], 'fleet_page')
self.assertFalse(page.get('skip'))
arcs = [widget['arc'] for widget in page['widgets'] if 'arc' in widget]
arcs = [widget['arc'] for widget in page['widgets']
if widget.get('arc', {}).get('id', '').endswith('_overview_arc')]
self.assertEqual([arc['id'][:-len('_overview_arc')] for arc in arcs],
[printer['id'] for printer in self.printers])
backings = [widget['arc'] for widget in page['widgets']
if widget.get('arc', {}).get('id', '').endswith('_overview_ring')]
self.assertEqual([arc['id'][:-len('_overview_ring')] for arc in backings],
[printer['id'] for printer in self.printers])
for backing, arc in zip(backings, arcs):
self.assertTrue(backing['hidden'])
self.assertEqual(backing['arc_color'], 0x6E767E)
self.assertGreater(backing['width'], arc['width'])
self.assertGreaterEqual(backing['x'], 0)
self.assertGreaterEqual(backing['y'], 0)
self.assertLessEqual(backing['x'] + backing['width'], 800)
self.assertLessEqual(backing['y'] + backing['height'], 480)
for arc, printer in zip(arcs, self.printers):
self.assertEqual(arc['on_click'], [{'lvgl.page.show': printer['id'] + '_page'}])
self.assertGreaterEqual(arc['width'], 120)
@@ -166,6 +179,7 @@ class FleetTests(unittest.TestCase):
for printer in self.printers:
self.assertIn(f'id({printer["id"]}_overview_arc)', code)
self.assertIn(f'id({printer["id"]}_overview_finish)', code)
self.assertIn(f'id({printer["id"]}_overview_ring)', code)
titles = [next(iter(widget.values())).get('text') for widget in page['widgets']]
self.assertIn('Print Status Overview', titles)
self.assertNotIn('fleet_summary', {next(iter(widget.values())).get('id') for widget in page['widgets']})