This commit is contained in:
Joshua King
2026-10-17 17:26:25 -04:00
parent 19adc71fef
commit f672de484e
14 changed files with 2946 additions and 1661 deletions
+53 -19
View File
@@ -10,11 +10,11 @@
namespace bambu_display {
constexpr uint32_t GREEN = 0x60DE9A;
constexpr uint32_t MUTED = 0x91A3B5;
constexpr uint32_t GREEN = 0x00FF00;
constexpr uint32_t MUTED = 0xAEB3B7;
constexpr uint32_t RED = 0xFF737B;
constexpr uint32_t AMBER = 0xF2C572;
constexpr uint32_t TRACK = 0x263442;
constexpr uint32_t TRACK = 0x19232B;
inline std::string lower(std::string text) {
for (char &c : text) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
@@ -67,6 +67,20 @@ inline std::string remaining(float value, const std::string &unit) {
return buffer;
}
inline int duration_seconds(float value, const std::string &unit) {
if (!std::isfinite(value) || value < 0) return -1;
double seconds;
if (unit == "h") seconds = double(value) * 3600;
else if (unit == "min") seconds = double(value) * 60;
else if (unit == "s") seconds = value;
else return -1;
return seconds <= 60000000 ? static_cast<int>(std::round(seconds)) : -1;
}
inline float celsius(float value, const std::string &unit) {
return unit == "°F" || unit == "F" ? (value - 32.0f) * 5.0f / 9.0f : value;
}
inline uint32_t filament_color(std::string value) {
if (!value.empty() && value[0] == '#') value.erase(0, 1);
// Bambu publishes RGB or RGBA, with alpha at the end.
@@ -89,8 +103,10 @@ struct Snapshot {
bool connected = false;
std::string online, status, task, stage, print_error, hms_error;
std::string remaining_unit, nozzle_unit, bed_unit;
std::string finish_time;
float progress = NAN, nozzle = NAN, nozzle_target = NAN, bed = NAN, bed_target = NAN;
float time_left = NAN, layer = NAN, total_layers = NAN, fan = NAN;
float aux_fan = NAN, exhaust_fan = NAN;
bool dual_nozzle = false;
float left_nozzle = NAN, left_target = NAN, right_nozzle = NAN, right_target = NAN;
std::vector<Tray> trays;
@@ -127,8 +143,10 @@ struct Snapshot {
namespace bambu_display {
struct TrayWidgets { lv_obj_t *card, *swatch, *label; };
struct GaugeWidgets { lv_obj_t *arc, *value, *detail; };
struct Widgets {
lv_obj_t *status, *task, *arc, *progress, *nozzle, *bed, *remaining, *layers, *stage;
lv_obj_t *status, *task, *top_progress, *remaining, *layers, *stage;
GaugeWidgets progress, nozzle, bed, fan, aux, exhaust;
std::vector<TrayWidgets> trays;
};
@@ -136,33 +154,49 @@ inline void set_text(lv_obj_t *label, const std::string &text) {
if (text != lv_label_get_text(label)) lv_label_set_text(label, text.c_str());
}
inline void render_gauge(const GaugeWidgets &widgets, float value, float position,
int maximum, const std::string &detail) {
lv_arc_set_value(widgets.arc, gauge_value(position, maximum));
set_text(widgets.value, number(value));
set_text(widgets.detail, detail);
}
inline void render(const Snapshot &data, const Widgets &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);
lv_obj_set_style_arc_color(widgets.arc, lv_color_hex(color), LV_PART_INDICATOR);
lv_arc_set_value(widgets.arc, live ? gauge_value(data.progress, 100) : 0);
set_text(widgets.progress, live && std::isfinite(data.progress)
? number(std::clamp(data.progress, 0.0f, 100.0f)) + "%" : "--");
lv_obj_set_style_arc_color(widgets.progress.arc, lv_color_hex(color), LV_PART_INDICATOR);
lv_bar_set_value(widgets.top_progress, live ? gauge_value(data.progress, 100) : 0, LV_ANIM_OFF);
render_gauge(widgets.progress, live && std::isfinite(data.progress)
? std::clamp(data.progress, 0.0f, 100.0f) : NAN, live ? data.progress : NAN, 100,
live ? remaining(data.time_left, data.remaining_unit) : "--");
set_text(widgets.task, live ? (valid_text(data.task) ? data.task : "No active print")
: data.connected ? "Waiting for printer data" : "Waiting for Home Assistant");
if (live && data.dual_nozzle) {
set_text(widgets.nozzle, "L " + temperature(data.left_nozzle, data.left_target, data.nozzle_unit)
+ "\nR " + temperature(data.right_nozzle, data.right_target, data.nozzle_unit));
} else {
set_text(widgets.nozzle, live ? temperature(data.nozzle, data.nozzle_target, data.nozzle_unit) : "--");
}
set_text(widgets.bed, live ? temperature(data.bed, data.bed_target, data.bed_unit) : "--");
set_text(widgets.remaining, live ? remaining(data.time_left, data.remaining_unit) : "--");
set_text(widgets.layers, live ? number(data.layer) + " / " + number(data.total_layers) : "--");
const float nozzle = data.dual_nozzle ? data.left_nozzle : data.nozzle;
const float target = data.dual_nozzle ? data.left_target : data.nozzle_target;
render_gauge(widgets.nozzle, live ? nozzle : NAN, live ? celsius(nozzle, data.nozzle_unit) : NAN,
300, live ? "/ " + number(target) + " " + data.nozzle_unit : "--");
render_gauge(widgets.bed, live ? data.bed : NAN, live ? celsius(data.bed, data.bed_unit) : NAN,
120, live ? "/ " + number(data.bed_target) + " " + data.bed_unit : "--");
render_gauge(widgets.fan, live ? data.fan : NAN, live ? data.fan : NAN, 100, "%");
if (data.dual_nozzle)
render_gauge(widgets.aux, live ? data.right_nozzle : NAN,
live ? celsius(data.right_nozzle, data.nozzle_unit) : NAN,
300, live ? "/ " + number(data.right_target) + " " + data.nozzle_unit : "--");
else
render_gauge(widgets.aux, live ? data.aux_fan : NAN, live ? data.aux_fan : NAN, 100, "%");
render_gauge(widgets.exhaust, live ? data.exhaust_fan : NAN, live ? data.exhaust_fan : NAN, 100, "%");
std::string time_line = live ? remaining(data.time_left, data.remaining_unit) : "--";
if (live && !data.finish_time.empty()) time_line = data.finish_time + " | " + time_line;
set_text(widgets.remaining, time_line);
set_text(widgets.layers, live ? "Layer " + number(data.layer) + " / " + number(data.total_layers) : "--");
std::string stage = live ? humanize(data.stage) : "Telemetry unavailable";
if (live && std::isfinite(data.fan)) stage += " | Part fan " + number(data.fan) + "%";
set_text(widgets.stage, stage);
for (size_t i = 0; i < std::min(data.trays.size(), widgets.trays.size()); ++i) {
const auto &tray = data.trays[i];
const auto &widget = widgets.trays[i];
set_text(widget.label, tray.label + " " + (live && valid_text(tray.type) ? tray.type : "--"));
set_text(widget.label, tray.label + "\n" + (live && valid_text(tray.type) ? tray.type : "--"));
lv_obj_set_style_bg_color(widget.swatch, lv_color_hex(live ? filament_color(tray.color) : TRACK), 0);
lv_obj_set_style_border_color(widget.card, lv_color_hex(live && is_on(tray.active) ? GREEN : TRACK), 0);
}