diff --git a/esphome/bambu-display/README.md b/esphome/bambu-display/README.md index de26f1c..2215300 100644 --- a/esphome/bambu-display/README.md +++ b/esphome/bambu-display/README.md @@ -21,7 +21,9 @@ The configured swipe order is: 2. Polly-Pocket — A1 mini, Office, external spool. 3. Ds-Turd-Flinger — A1, Upstairs, four-slot AMS. 4. Jordyns-Layer-Slayer — A1, Upstairs, four-slot AMS. -5. Constipation-Orion — Office, left/right nozzles and four-slot AMS. +5. Constipation-Orion — Office, left/right nozzles and both four-slot AMS units. + AMS A uses `constipation_orion_ams_1`; AMS B retains its original + `h2c_31b8ap5a2200288_ams_2` entity prefix. Swipe left for the next printer or right for the previous printer. Navigation wraps from the last page to the first. Previous/Next buttons provide the same navigation. @@ -34,9 +36,25 @@ stage, fan speeds and filament colors/types. The layout follows BambuHelper's classic gauges: black background, bright green accents, six arc gauges and a vertical AMS column. Nozzle arcs are orange and the bed arc is cyan. For dual-nozzle printers the auxiliary-fan position becomes the right-nozzle gauge. The active -filament slot has a green outline. Print errors -and HMS alerts change the status badge to red; paused prints use amber. The display -shows an alert badge rather than the detailed HMS diagnostic text. +filament slot has a green outline. Print errors use red; paused prints use amber. + +**HMS means Health Management System.** Its binary sensor means the Bambu integration +has one or more printer health notifications, which can include information or +maintenance messages as well as faults. Tap the status in the upper-right corner +or the scrolling message above the footer to open the health-details panel. It +shows the codes, severities and descriptions reported by Home Assistant, for up to +three current HMS notices, plus print-error details. Scroll vertically for long +messages and tap Close to return. If more than three notices exist, the panel says +how many additional notices are available in Home Assistant. + +The status uses **HEALTH INFO** (cyan) for information-only notifications, +**HEALTH NOTICE** (amber) for common/unknown severities or incomplete details, and +**HEALTH ERROR** (red) for fatal/serious severities. Severity comes from the Bambu +integration's `N-Severity` attributes. Missing or unloaded messages never establish +an information-only status. The display does not dismiss or clear printer alerts. +The [`hms_errors` sensor attributes](https://github.com/greghesp/ha-bambulab/blob/main/custom_components/bambu_lab/pybambu/models.py) +provide `Count`, `1-Code`, `1-Error`, `1-Severity`, and the corresponding numbered +fields for subsequent notices. Stale attributes are ignored when the sensor is off. When Home Assistant disconnects or a printer is offline, its live metrics are cleared to `--` rather than continuing to show cached values as live. Unknown individual @@ -140,7 +158,8 @@ python esphome/bambu-display/preview.py ``` `preview.py` compiles the same generated LVGL widgets for ESPHome's SDL host with -clearly synthetic telemetry, then captures all five pages into `previews/`. It needs +clearly synthetic telemetry, then captures all five pages and a synthetic health +notice into `previews/`. The demo notice is not a diagnosis of any real printer. It needs ESPHome, Pillow, a C++ compiler and SDL2 (`sdl2-config` on PATH). It runs headlessly, without connecting to Home Assistant or any printer. It also injects LVGL pointer samples to check left/right swipes, page wraparound and consumption of a swipe that diff --git a/esphome/bambu-display/dashboard.h b/esphome/bambu-display/dashboard.h index cdc5017..8143e1e 100644 --- a/esphome/bambu-display/dashboard.h +++ b/esphome/bambu-display/dashboard.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -99,9 +100,13 @@ struct Tray { std::string label, type, color, active; }; +struct HealthNotice { std::string code, message, severity; }; + struct Snapshot { bool connected = false; std::string online, status, task, stage, print_error, hms_error; + std::string hms_count, print_error_message, print_error_code; + std::vector health_notices; 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; @@ -112,12 +117,70 @@ struct Snapshot { std::vector trays; bool live() const { return connected && is_on(online) && valid_text(status); } + int notice_count() const { + int loaded = 0; + for (const auto ¬ice : health_notices) + if (valid_text(notice.code) || valid_text(notice.message)) ++loaded; + char *end; + const long count = std::strtol(hms_count.c_str(), &end, 10); + return end != hms_count.c_str() && *end == '\0' && count >= 0 && count < 10000 + ? std::max(loaded, static_cast(count)) : loaded; + } + int health_severity_rank() const { + int rank = 1, loaded = 0; + for (const auto ¬ice : health_notices) { + if (!valid_text(notice.code) && !valid_text(notice.message)) continue; + ++loaded; + const auto severity = lower(notice.severity); + rank = std::max(rank, severity == "fatal" || severity == "serious" ? 3 : severity == "info" ? 1 : 2); + } + // Missing/unloaded messages cannot establish an info-only notification. + return loaded == 0 || notice_count() > loaded ? std::max(rank, 2) : rank; + } + std::string alert_summary() const { + if (is_on(print_error)) + return valid_text(print_error_message) ? print_error_message : "Print error reported; tap status for details"; + if (is_on(hms_error)) { + for (const auto ¬ice : health_notices) + if (valid_text(notice.message)) return notice.message; + return "Printer health notification; tap status for details"; + } + return ""; + } + std::string health_details() const { + if (!live()) return "Printer health data is unavailable. Waiting for a live connection."; + std::string details; + if (is_on(print_error)) { + details = "PRINT ERROR\n"; + if (valid_text(print_error_code)) details += print_error_code + "\n"; + details += valid_text(print_error_message) ? print_error_message : "Message not supplied by Home Assistant."; + details += "\n\n"; + } + if (is_on(hms_error)) { + details += "HMS = Health Management System\n"; + int loaded = 0; + for (const auto ¬ice : health_notices) { + if (!valid_text(notice.code) && !valid_text(notice.message)) continue; + details += "\nNotice " + std::to_string(++loaded); + if (valid_text(notice.severity)) details += " / " + notice.severity; + details += "\n"; + if (valid_text(notice.code)) details += notice.code + "\n"; + details += valid_text(notice.message) ? notice.message : "Message not supplied by Home Assistant."; + details += "\n"; + } + if (loaded == 0) details += "\nNotification is active; details have not arrived from Home Assistant.\n"; + if (notice_count() > loaded) + details += "\n" + std::to_string(notice_count() - loaded) + " more notification(s). View all in Home Assistant.\n"; + } + return details.empty() ? "No current printer health notifications." : details; + } std::string status_text() const { if (!connected) return "HA OFFLINE"; if (lower(online) == "off" || lower(online) == "false") return "PRINTER OFFLINE"; if (!live()) return "SYNCING"; if (is_on(print_error)) return "PRINT ERROR"; - if (is_on(hms_error)) return "HMS ALERT"; + if (is_on(hms_error)) + return health_severity_rank() >= 3 ? "HEALTH ERROR" : health_severity_rank() == 1 ? "HEALTH INFO" : "HEALTH NOTICE"; const auto state = lower(status); if (state == "running") return "PRINTING"; if (state == "pause" || state == "paused") return "PAUSED"; @@ -129,7 +192,9 @@ struct Snapshot { uint32_t status_color() const { if (!live()) return MUTED; const auto state = lower(status); - if (is_on(print_error) || is_on(hms_error) || state == "failed") return RED; + if (is_on(print_error) || state == "failed") return RED; + if (is_on(hms_error)) + return health_severity_rank() >= 3 ? RED : health_severity_rank() == 1 ? 0x00E5E5 : AMBER; if (state == "pause" || state == "paused") return AMBER; return GREEN; } @@ -145,7 +210,7 @@ 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, *top_progress, *remaining, *layers, *stage; + lv_obj_t *status, *task, *top_progress, *remaining, *layers, *stage, *health_details; GaugeWidgets progress, nozzle, bed, fan, aux, exhaust; std::vector trays; }; @@ -192,7 +257,10 @@ inline void render(const Snapshot &data, const Widgets &widgets) { 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 && (is_on(data.print_error) || is_on(data.hms_error))) stage = data.alert_summary(); set_text(widgets.stage, stage); + lv_obj_set_style_text_color(widgets.stage, lv_color_hex(live && !data.alert_summary().empty() ? color : MUTED), 0); + set_text(widgets.health_details, data.health_details()); 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]; diff --git a/esphome/bambu-display/generate.py b/esphome/bambu-display/generate.py index 5c2b0dd..05013f3 100644 --- a/esphome/bambu-display/generate.py +++ b/esphome/bambu-display/generate.py @@ -104,7 +104,8 @@ def page_for(printer, index, total): 'indicator': {'bg_color': GREEN}, 'gesture_bubble': True}}, label(printer['name'], 16, 17, 485, 26, text_color=GREEN), label(f'{index + 1} / {total}', 520, 24, 70, 14, text_color=MUTED, text_align='CENTER'), - label('SYNCING', 598, 21, 186, 16, id=f'{pid}_status_label', text_align='RIGHT', text_color=MUTED), + label('SYNCING', 598, 21, 186, 16, id=f'{pid}_status_label', text_align='RIGHT', text_color=MUTED, + clickable=True, on_click=[{'lvgl.widget.show': f'{pid}_health_panel'}]), {'obj': {'x': 578, 'y': 64, 'width': 1, 'height': 362, 'bg_color': TRACK, 'border_width': 0, 'pad_all': 0, 'scrollable': False, 'gesture_bubble': True}}, ] @@ -119,7 +120,9 @@ def page_for(printer, index, total): widgets += gauge(pid, 'exhaust', 'Exhaust', 388, 229, GREEN, 100) widgets += [ label('--', 16, 401, 544, 26, id=f'{pid}_remaining_label', text_color=GREEN, text_align='CENTER'), - label('Telemetry unavailable', 16, 430, 544, 12, id=f'{pid}_stage_label', text_color=MUTED, text_align='CENTER'), + label('Telemetry unavailable', 16, 430, 544, 12, id=f'{pid}_stage_label', text_color=MUTED, + text_align='CENTER', long_mode='SCROLL_CIRCULAR', clickable=True, + on_click=[{'lvgl.widget.show': f'{pid}_health_panel'}]), label('--', 594, 397, 190, 16, id=f'{pid}_layers_label', text_color=MUTED, text_align='CENTER'), ] trays = trays_for(printer) @@ -153,6 +156,22 @@ def page_for(printer, index, total): 'on_click': [{f'lvgl.page.{direction}': {}}]}}) widgets.append(label('Waiting for Home Assistant', 94, 449, 612, 14, id=f'{pid}_task_label', text_align='CENTER')) + widgets.append({'obj': { + 'id': f'{pid}_health_panel', 'x': 24, 'y': 62, 'width': 752, 'height': 366, + 'hidden': True, 'bg_color': BG, 'bg_opa': 'COVER', 'border_width': 2, + 'border_color': MUTED, 'radius': 10, 'pad_all': 0, 'scrollable': False, + 'gesture_bubble': False, + 'widgets': [ + label('Printer health / ' + printer['name'], 16, 14, 716, 20), + {'obj': {'x': 12, 'y': 50, 'width': 724, 'height': 246, + 'bg_color': BG, 'border_width': 0, 'pad_all': 0, + 'scroll_dir': 'VER', 'scrollbar_mode': 'AUTO', 'gesture_bubble': False, + 'widgets': [label('No current health notifications.', 0, 0, 700, 18, + id=f'{pid}_health_details', long_mode='WRAP', height='SIZE_CONTENT')]}}, + {'button': {'x': 624, 'y': 310, 'width': 108, 'height': 40, 'bg_color': TRACK, + 'radius': 6, 'widgets': [{'label': {'text': 'CLOSE', 'align': 'CENTER'}}], + 'on_click': [{'lvgl.widget.hide': f'{pid}_health_panel'}]}}, + ]}}) return {'id': f'{pid}_page', 'bg_color': BG, 'bg_opa': 'COVER', 'pad_all': 0, 'scrollable': False, 'scrollbar_mode': 'OFF', 'on_swipe_left': [ @@ -203,6 +222,14 @@ def generate(printers): text_fields = {key: (entity(printer, key, suffix), None) for key, suffix in TEXT.items()} text_fields.update({key: (entity(printer, key, suffix, 'binary_sensor'), None) for key, suffix in BINARY.items()}) + hms_entity = entity(printer, 'hms_error', BINARY['hms_error'], 'binary_sensor') + text_fields['hms_count'] = (hms_entity, 'Count') + for number in range(1, 4): + for key, attribute in [('message', 'Error'), ('code', 'Code'), ('severity', 'Severity')]: + text_fields[f'hms_{number}_{key}'] = (hms_entity, f'{number}-{attribute}') + print_error_entity = entity(printer, 'print_error', BINARY['print_error'], 'binary_sensor') + text_fields['print_error_message'] = (print_error_entity, 'error') + text_fields['print_error_code'] = (print_error_entity, 'code') for key, numeric_key in [('remaining_unit', 'time_left'), ('nozzle_unit', 'nozzle'), ('bed_unit', 'bed')]: text_fields[key] = (entity(printer, numeric_key, NUMERIC[numeric_key]), 'unit_of_measurement') trays = trays_for(printer) @@ -219,8 +246,11 @@ def generate(printers): lines = ['bambu_display::Snapshot data;', 'data.connected = id(ha_api).is_connected_with_state_subscription();'] for key, expr in expressions.items(): - if not key.startswith('tray_'): + if not key.startswith(('tray_', 'hms_1_', 'hms_2_', 'hms_3_')): lines.append(f'data.{key} = {expr};') + for number in range(1, 4): + lines.append('data.health_notices.push_back({' + ', '.join( + expressions[f'hms_{number}_{key}'] for key in ('code', 'message', 'severity')) + '});') if printer.get('dual_nozzle'): lines.append('data.dual_nozzle = true;') lines += [ @@ -237,7 +267,8 @@ def generate(printers): tray_widgets.append('{' + ', '.join(f'id({pid}_tray_{i}_{part})' for part in ('card', 'swatch', 'label')) + '}') refs = [f'id({pid}_{key})' for key in ( - 'status_label', 'task_label', 'top_progress', 'remaining_label', 'layers_label', 'stage_label')] + 'status_label', 'task_label', 'top_progress', 'remaining_label', 'layers_label', 'stage_label', + 'health_details')] for key in ('progress', 'nozzle', 'bed', 'fan', 'aux', 'exhaust'): refs.append('{' + ', '.join(f'id({pid}_{key}_{part})' for part in ('arc', 'label', 'detail')) + '}') refs.append('{' + ', '.join(tray_widgets) + '}') diff --git a/esphome/bambu-display/generated.yaml b/esphome/bambu-display/generated.yaml index 62432f8..74c233f 100644 --- a/esphome/bambu-display/generated.yaml +++ b/esphome/bambu-display/generated.yaml @@ -233,6 +233,66 @@ text_sensor: id: prints_charming_hms_error entity_id: binary_sensor.p1s_01p09c470102673_hms_errors internal: true +- platform: homeassistant + id: prints_charming_hms_count + entity_id: binary_sensor.p1s_01p09c470102673_hms_errors + internal: true + attribute: Count +- platform: homeassistant + id: prints_charming_hms_1_message + entity_id: binary_sensor.p1s_01p09c470102673_hms_errors + internal: true + attribute: 1-Error +- platform: homeassistant + id: prints_charming_hms_1_code + entity_id: binary_sensor.p1s_01p09c470102673_hms_errors + internal: true + attribute: 1-Code +- platform: homeassistant + id: prints_charming_hms_1_severity + entity_id: binary_sensor.p1s_01p09c470102673_hms_errors + internal: true + attribute: 1-Severity +- platform: homeassistant + id: prints_charming_hms_2_message + entity_id: binary_sensor.p1s_01p09c470102673_hms_errors + internal: true + attribute: 2-Error +- platform: homeassistant + id: prints_charming_hms_2_code + entity_id: binary_sensor.p1s_01p09c470102673_hms_errors + internal: true + attribute: 2-Code +- platform: homeassistant + id: prints_charming_hms_2_severity + entity_id: binary_sensor.p1s_01p09c470102673_hms_errors + internal: true + attribute: 2-Severity +- platform: homeassistant + id: prints_charming_hms_3_message + entity_id: binary_sensor.p1s_01p09c470102673_hms_errors + internal: true + attribute: 3-Error +- platform: homeassistant + id: prints_charming_hms_3_code + entity_id: binary_sensor.p1s_01p09c470102673_hms_errors + internal: true + attribute: 3-Code +- platform: homeassistant + id: prints_charming_hms_3_severity + entity_id: binary_sensor.p1s_01p09c470102673_hms_errors + internal: true + attribute: 3-Severity +- platform: homeassistant + id: prints_charming_print_error_message + entity_id: binary_sensor.p1s_01p09c470102673_print_error + internal: true + attribute: error +- platform: homeassistant + id: prints_charming_print_error_code + entity_id: binary_sensor.p1s_01p09c470102673_print_error + internal: true + attribute: code - platform: homeassistant id: prints_charming_remaining_unit entity_id: sensor.p1s_01p09c470102673_remaining_time @@ -392,6 +452,66 @@ text_sensor: id: polly_pocket_hms_error entity_id: binary_sensor.a1mini_0309ca580403653_hms_errors internal: true +- platform: homeassistant + id: polly_pocket_hms_count + entity_id: binary_sensor.a1mini_0309ca580403653_hms_errors + internal: true + attribute: Count +- platform: homeassistant + id: polly_pocket_hms_1_message + entity_id: binary_sensor.a1mini_0309ca580403653_hms_errors + internal: true + attribute: 1-Error +- platform: homeassistant + id: polly_pocket_hms_1_code + entity_id: binary_sensor.a1mini_0309ca580403653_hms_errors + internal: true + attribute: 1-Code +- platform: homeassistant + id: polly_pocket_hms_1_severity + entity_id: binary_sensor.a1mini_0309ca580403653_hms_errors + internal: true + attribute: 1-Severity +- platform: homeassistant + id: polly_pocket_hms_2_message + entity_id: binary_sensor.a1mini_0309ca580403653_hms_errors + internal: true + attribute: 2-Error +- platform: homeassistant + id: polly_pocket_hms_2_code + entity_id: binary_sensor.a1mini_0309ca580403653_hms_errors + internal: true + attribute: 2-Code +- platform: homeassistant + id: polly_pocket_hms_2_severity + entity_id: binary_sensor.a1mini_0309ca580403653_hms_errors + internal: true + attribute: 2-Severity +- platform: homeassistant + id: polly_pocket_hms_3_message + entity_id: binary_sensor.a1mini_0309ca580403653_hms_errors + internal: true + attribute: 3-Error +- platform: homeassistant + id: polly_pocket_hms_3_code + entity_id: binary_sensor.a1mini_0309ca580403653_hms_errors + internal: true + attribute: 3-Code +- platform: homeassistant + id: polly_pocket_hms_3_severity + entity_id: binary_sensor.a1mini_0309ca580403653_hms_errors + internal: true + attribute: 3-Severity +- platform: homeassistant + id: polly_pocket_print_error_message + entity_id: binary_sensor.a1mini_0309ca580403653_print_error + internal: true + attribute: error +- platform: homeassistant + id: polly_pocket_print_error_code + entity_id: binary_sensor.a1mini_0309ca580403653_print_error + internal: true + attribute: code - platform: homeassistant id: polly_pocket_remaining_unit entity_id: sensor.a1mini_0309ca580403653_remaining_time @@ -446,6 +566,66 @@ text_sensor: id: ds_turd_flinger_hms_error entity_id: binary_sensor.a1_03919d540806387_hms_errors internal: true +- platform: homeassistant + id: ds_turd_flinger_hms_count + entity_id: binary_sensor.a1_03919d540806387_hms_errors + internal: true + attribute: Count +- platform: homeassistant + id: ds_turd_flinger_hms_1_message + entity_id: binary_sensor.a1_03919d540806387_hms_errors + internal: true + attribute: 1-Error +- platform: homeassistant + id: ds_turd_flinger_hms_1_code + entity_id: binary_sensor.a1_03919d540806387_hms_errors + internal: true + attribute: 1-Code +- platform: homeassistant + id: ds_turd_flinger_hms_1_severity + entity_id: binary_sensor.a1_03919d540806387_hms_errors + internal: true + attribute: 1-Severity +- platform: homeassistant + id: ds_turd_flinger_hms_2_message + entity_id: binary_sensor.a1_03919d540806387_hms_errors + internal: true + attribute: 2-Error +- platform: homeassistant + id: ds_turd_flinger_hms_2_code + entity_id: binary_sensor.a1_03919d540806387_hms_errors + internal: true + attribute: 2-Code +- platform: homeassistant + id: ds_turd_flinger_hms_2_severity + entity_id: binary_sensor.a1_03919d540806387_hms_errors + internal: true + attribute: 2-Severity +- platform: homeassistant + id: ds_turd_flinger_hms_3_message + entity_id: binary_sensor.a1_03919d540806387_hms_errors + internal: true + attribute: 3-Error +- platform: homeassistant + id: ds_turd_flinger_hms_3_code + entity_id: binary_sensor.a1_03919d540806387_hms_errors + internal: true + attribute: 3-Code +- platform: homeassistant + id: ds_turd_flinger_hms_3_severity + entity_id: binary_sensor.a1_03919d540806387_hms_errors + internal: true + attribute: 3-Severity +- platform: homeassistant + id: ds_turd_flinger_print_error_message + entity_id: binary_sensor.a1_03919d540806387_print_error + internal: true + attribute: error +- platform: homeassistant + id: ds_turd_flinger_print_error_code + entity_id: binary_sensor.a1_03919d540806387_print_error + internal: true + attribute: code - platform: homeassistant id: ds_turd_flinger_remaining_unit entity_id: sensor.a1_03919d540806387_remaining_time @@ -545,6 +725,66 @@ text_sensor: id: jordyns_layer_slayer_hms_error entity_id: binary_sensor.jordyns_layer_slayer_hms_errors internal: true +- platform: homeassistant + id: jordyns_layer_slayer_hms_count + entity_id: binary_sensor.jordyns_layer_slayer_hms_errors + internal: true + attribute: Count +- platform: homeassistant + id: jordyns_layer_slayer_hms_1_message + entity_id: binary_sensor.jordyns_layer_slayer_hms_errors + internal: true + attribute: 1-Error +- platform: homeassistant + id: jordyns_layer_slayer_hms_1_code + entity_id: binary_sensor.jordyns_layer_slayer_hms_errors + internal: true + attribute: 1-Code +- platform: homeassistant + id: jordyns_layer_slayer_hms_1_severity + entity_id: binary_sensor.jordyns_layer_slayer_hms_errors + internal: true + attribute: 1-Severity +- platform: homeassistant + id: jordyns_layer_slayer_hms_2_message + entity_id: binary_sensor.jordyns_layer_slayer_hms_errors + internal: true + attribute: 2-Error +- platform: homeassistant + id: jordyns_layer_slayer_hms_2_code + entity_id: binary_sensor.jordyns_layer_slayer_hms_errors + internal: true + attribute: 2-Code +- platform: homeassistant + id: jordyns_layer_slayer_hms_2_severity + entity_id: binary_sensor.jordyns_layer_slayer_hms_errors + internal: true + attribute: 2-Severity +- platform: homeassistant + id: jordyns_layer_slayer_hms_3_message + entity_id: binary_sensor.jordyns_layer_slayer_hms_errors + internal: true + attribute: 3-Error +- platform: homeassistant + id: jordyns_layer_slayer_hms_3_code + entity_id: binary_sensor.jordyns_layer_slayer_hms_errors + internal: true + attribute: 3-Code +- platform: homeassistant + id: jordyns_layer_slayer_hms_3_severity + entity_id: binary_sensor.jordyns_layer_slayer_hms_errors + internal: true + attribute: 3-Severity +- platform: homeassistant + id: jordyns_layer_slayer_print_error_message + entity_id: binary_sensor.jordyns_layer_slayer_print_error + internal: true + attribute: error +- platform: homeassistant + id: jordyns_layer_slayer_print_error_code + entity_id: binary_sensor.jordyns_layer_slayer_print_error + internal: true + attribute: code - platform: homeassistant id: jordyns_layer_slayer_remaining_unit entity_id: sensor.jordyns_layer_slayer_remaining_time @@ -644,6 +884,66 @@ text_sensor: id: constipation_orion_hms_error entity_id: binary_sensor.constipation_orion_hms_errors internal: true +- platform: homeassistant + id: constipation_orion_hms_count + entity_id: binary_sensor.constipation_orion_hms_errors + internal: true + attribute: Count +- platform: homeassistant + id: constipation_orion_hms_1_message + entity_id: binary_sensor.constipation_orion_hms_errors + internal: true + attribute: 1-Error +- platform: homeassistant + id: constipation_orion_hms_1_code + entity_id: binary_sensor.constipation_orion_hms_errors + internal: true + attribute: 1-Code +- platform: homeassistant + id: constipation_orion_hms_1_severity + entity_id: binary_sensor.constipation_orion_hms_errors + internal: true + attribute: 1-Severity +- platform: homeassistant + id: constipation_orion_hms_2_message + entity_id: binary_sensor.constipation_orion_hms_errors + internal: true + attribute: 2-Error +- platform: homeassistant + id: constipation_orion_hms_2_code + entity_id: binary_sensor.constipation_orion_hms_errors + internal: true + attribute: 2-Code +- platform: homeassistant + id: constipation_orion_hms_2_severity + entity_id: binary_sensor.constipation_orion_hms_errors + internal: true + attribute: 2-Severity +- platform: homeassistant + id: constipation_orion_hms_3_message + entity_id: binary_sensor.constipation_orion_hms_errors + internal: true + attribute: 3-Error +- platform: homeassistant + id: constipation_orion_hms_3_code + entity_id: binary_sensor.constipation_orion_hms_errors + internal: true + attribute: 3-Code +- platform: homeassistant + id: constipation_orion_hms_3_severity + entity_id: binary_sensor.constipation_orion_hms_errors + internal: true + attribute: 3-Severity +- platform: homeassistant + id: constipation_orion_print_error_message + entity_id: binary_sensor.constipation_orion_print_error + internal: true + attribute: error +- platform: homeassistant + id: constipation_orion_print_error_code + entity_id: binary_sensor.constipation_orion_print_error + internal: true + attribute: code - platform: homeassistant id: constipation_orion_remaining_unit entity_id: sensor.constipation_orion_remaining_time @@ -719,6 +1019,66 @@ text_sensor: entity_id: sensor.constipation_orion_ams_1_tray_4 internal: true attribute: active +- platform: homeassistant + id: constipation_orion_tray_4_type + entity_id: sensor.h2c_31b8ap5a2200288_ams_2_tray_1 + internal: true + attribute: type +- platform: homeassistant + id: constipation_orion_tray_4_color + entity_id: sensor.h2c_31b8ap5a2200288_ams_2_tray_1 + internal: true + attribute: color +- platform: homeassistant + id: constipation_orion_tray_4_active + entity_id: sensor.h2c_31b8ap5a2200288_ams_2_tray_1 + internal: true + attribute: active +- platform: homeassistant + id: constipation_orion_tray_5_type + entity_id: sensor.h2c_31b8ap5a2200288_ams_2_tray_2 + internal: true + attribute: type +- platform: homeassistant + id: constipation_orion_tray_5_color + entity_id: sensor.h2c_31b8ap5a2200288_ams_2_tray_2 + internal: true + attribute: color +- platform: homeassistant + id: constipation_orion_tray_5_active + entity_id: sensor.h2c_31b8ap5a2200288_ams_2_tray_2 + internal: true + attribute: active +- platform: homeassistant + id: constipation_orion_tray_6_type + entity_id: sensor.h2c_31b8ap5a2200288_ams_2_tray_3 + internal: true + attribute: type +- platform: homeassistant + id: constipation_orion_tray_6_color + entity_id: sensor.h2c_31b8ap5a2200288_ams_2_tray_3 + internal: true + attribute: color +- platform: homeassistant + id: constipation_orion_tray_6_active + entity_id: sensor.h2c_31b8ap5a2200288_ams_2_tray_3 + internal: true + attribute: active +- platform: homeassistant + id: constipation_orion_tray_7_type + entity_id: sensor.h2c_31b8ap5a2200288_ams_2_tray_4 + internal: true + attribute: type +- platform: homeassistant + id: constipation_orion_tray_7_color + entity_id: sensor.h2c_31b8ap5a2200288_ams_2_tray_4 + internal: true + attribute: color +- platform: homeassistant + id: constipation_orion_tray_7_active + entity_id: sensor.h2c_31b8ap5a2200288_ams_2_tray_4 + internal: true + attribute: active script: - id: prints_charming_refresh then: @@ -742,9 +1102,15 @@ script: data.online = id(prints_charming_online).state; data.print_error = id(prints_charming_print_error).state; data.hms_error = id(prints_charming_hms_error).state; + data.hms_count = id(prints_charming_hms_count).state; + data.print_error_message = id(prints_charming_print_error_message).state; + data.print_error_code = id(prints_charming_print_error_code).state; data.remaining_unit = id(prints_charming_remaining_unit).state; data.nozzle_unit = id(prints_charming_nozzle_unit).state; data.bed_unit = id(prints_charming_bed_unit).state; + data.health_notices.push_back({id(prints_charming_hms_1_code).state, id(prints_charming_hms_1_message).state, id(prints_charming_hms_1_severity).state}); + data.health_notices.push_back({id(prints_charming_hms_2_code).state, id(prints_charming_hms_2_message).state, id(prints_charming_hms_2_severity).state}); + data.health_notices.push_back({id(prints_charming_hms_3_code).state, id(prints_charming_hms_3_message).state, id(prints_charming_hms_3_severity).state}); const int seconds_left = bambu_display::duration_seconds(data.time_left, data.remaining_unit); const auto clock = id(ha_time).now(); if (clock.is_valid() && seconds_left >= 0) @@ -757,7 +1123,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(data, {id(prints_charming_status_label), id(prints_charming_task_label), id(prints_charming_top_progress), id(prints_charming_remaining_label), id(prints_charming_layers_label), id(prints_charming_stage_label), {id(prints_charming_progress_arc), id(prints_charming_progress_label), id(prints_charming_progress_detail)}, {id(prints_charming_nozzle_arc), id(prints_charming_nozzle_label), id(prints_charming_nozzle_detail)}, {id(prints_charming_bed_arc), id(prints_charming_bed_label), id(prints_charming_bed_detail)}, {id(prints_charming_fan_arc), id(prints_charming_fan_label), id(prints_charming_fan_detail)}, {id(prints_charming_aux_arc), id(prints_charming_aux_label), id(prints_charming_aux_detail)}, {id(prints_charming_exhaust_arc), id(prints_charming_exhaust_label), id(prints_charming_exhaust_detail)}, {{id(prints_charming_tray_0_card), id(prints_charming_tray_0_swatch), id(prints_charming_tray_0_label)}, {id(prints_charming_tray_1_card), id(prints_charming_tray_1_swatch), id(prints_charming_tray_1_label)}, {id(prints_charming_tray_2_card), id(prints_charming_tray_2_swatch), id(prints_charming_tray_2_label)}, {id(prints_charming_tray_3_card), id(prints_charming_tray_3_swatch), id(prints_charming_tray_3_label)}, {id(prints_charming_tray_4_card), id(prints_charming_tray_4_swatch), id(prints_charming_tray_4_label)}, {id(prints_charming_tray_5_card), id(prints_charming_tray_5_swatch), id(prints_charming_tray_5_label)}, {id(prints_charming_tray_6_card), id(prints_charming_tray_6_swatch), id(prints_charming_tray_6_label)}, {id(prints_charming_tray_7_card), id(prints_charming_tray_7_swatch), id(prints_charming_tray_7_label)}}}); + bambu_display::render(data, {id(prints_charming_status_label), id(prints_charming_task_label), id(prints_charming_top_progress), id(prints_charming_remaining_label), id(prints_charming_layers_label), id(prints_charming_stage_label), id(prints_charming_health_details), {id(prints_charming_progress_arc), id(prints_charming_progress_label), id(prints_charming_progress_detail)}, {id(prints_charming_nozzle_arc), id(prints_charming_nozzle_label), id(prints_charming_nozzle_detail)}, {id(prints_charming_bed_arc), id(prints_charming_bed_label), id(prints_charming_bed_detail)}, {id(prints_charming_fan_arc), id(prints_charming_fan_label), id(prints_charming_fan_detail)}, {id(prints_charming_aux_arc), id(prints_charming_aux_label), id(prints_charming_aux_detail)}, {id(prints_charming_exhaust_arc), id(prints_charming_exhaust_label), id(prints_charming_exhaust_detail)}, {{id(prints_charming_tray_0_card), id(prints_charming_tray_0_swatch), id(prints_charming_tray_0_label)}, {id(prints_charming_tray_1_card), id(prints_charming_tray_1_swatch), id(prints_charming_tray_1_label)}, {id(prints_charming_tray_2_card), id(prints_charming_tray_2_swatch), id(prints_charming_tray_2_label)}, {id(prints_charming_tray_3_card), id(prints_charming_tray_3_swatch), id(prints_charming_tray_3_label)}, {id(prints_charming_tray_4_card), id(prints_charming_tray_4_swatch), id(prints_charming_tray_4_label)}, {id(prints_charming_tray_5_card), id(prints_charming_tray_5_swatch), id(prints_charming_tray_5_label)}, {id(prints_charming_tray_6_card), id(prints_charming_tray_6_swatch), id(prints_charming_tray_6_label)}, {id(prints_charming_tray_7_card), id(prints_charming_tray_7_swatch), id(prints_charming_tray_7_label)}}}); - id: polly_pocket_refresh then: - lambda: !lambda |- @@ -778,15 +1144,21 @@ script: data.online = id(polly_pocket_online).state; data.print_error = id(polly_pocket_print_error).state; data.hms_error = id(polly_pocket_hms_error).state; + data.hms_count = id(polly_pocket_hms_count).state; + data.print_error_message = id(polly_pocket_print_error_message).state; + data.print_error_code = id(polly_pocket_print_error_code).state; data.remaining_unit = id(polly_pocket_remaining_unit).state; data.nozzle_unit = id(polly_pocket_nozzle_unit).state; data.bed_unit = id(polly_pocket_bed_unit).state; + data.health_notices.push_back({id(polly_pocket_hms_1_code).state, id(polly_pocket_hms_1_message).state, id(polly_pocket_hms_1_severity).state}); + data.health_notices.push_back({id(polly_pocket_hms_2_code).state, id(polly_pocket_hms_2_message).state, id(polly_pocket_hms_2_severity).state}); + data.health_notices.push_back({id(polly_pocket_hms_3_code).state, id(polly_pocket_hms_3_message).state, id(polly_pocket_hms_3_severity).state}); const int seconds_left = bambu_display::duration_seconds(data.time_left, data.remaining_unit); 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"); 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(data, {id(polly_pocket_status_label), id(polly_pocket_task_label), id(polly_pocket_top_progress), id(polly_pocket_remaining_label), id(polly_pocket_layers_label), id(polly_pocket_stage_label), {id(polly_pocket_progress_arc), id(polly_pocket_progress_label), id(polly_pocket_progress_detail)}, {id(polly_pocket_nozzle_arc), id(polly_pocket_nozzle_label), id(polly_pocket_nozzle_detail)}, {id(polly_pocket_bed_arc), id(polly_pocket_bed_label), id(polly_pocket_bed_detail)}, {id(polly_pocket_fan_arc), id(polly_pocket_fan_label), id(polly_pocket_fan_detail)}, {id(polly_pocket_aux_arc), id(polly_pocket_aux_label), id(polly_pocket_aux_detail)}, {id(polly_pocket_exhaust_arc), id(polly_pocket_exhaust_label), id(polly_pocket_exhaust_detail)}, {{id(polly_pocket_tray_0_card), id(polly_pocket_tray_0_swatch), id(polly_pocket_tray_0_label)}}}); + bambu_display::render(data, {id(polly_pocket_status_label), id(polly_pocket_task_label), id(polly_pocket_top_progress), id(polly_pocket_remaining_label), id(polly_pocket_layers_label), id(polly_pocket_stage_label), id(polly_pocket_health_details), {id(polly_pocket_progress_arc), id(polly_pocket_progress_label), id(polly_pocket_progress_detail)}, {id(polly_pocket_nozzle_arc), id(polly_pocket_nozzle_label), id(polly_pocket_nozzle_detail)}, {id(polly_pocket_bed_arc), id(polly_pocket_bed_label), id(polly_pocket_bed_detail)}, {id(polly_pocket_fan_arc), id(polly_pocket_fan_label), id(polly_pocket_fan_detail)}, {id(polly_pocket_aux_arc), id(polly_pocket_aux_label), id(polly_pocket_aux_detail)}, {id(polly_pocket_exhaust_arc), id(polly_pocket_exhaust_label), id(polly_pocket_exhaust_detail)}, {{id(polly_pocket_tray_0_card), id(polly_pocket_tray_0_swatch), id(polly_pocket_tray_0_label)}}}); - id: ds_turd_flinger_refresh then: - lambda: !lambda |- @@ -807,9 +1179,15 @@ script: data.online = id(ds_turd_flinger_online).state; data.print_error = id(ds_turd_flinger_print_error).state; data.hms_error = id(ds_turd_flinger_hms_error).state; + data.hms_count = id(ds_turd_flinger_hms_count).state; + data.print_error_message = id(ds_turd_flinger_print_error_message).state; + data.print_error_code = id(ds_turd_flinger_print_error_code).state; data.remaining_unit = id(ds_turd_flinger_remaining_unit).state; data.nozzle_unit = id(ds_turd_flinger_nozzle_unit).state; data.bed_unit = id(ds_turd_flinger_bed_unit).state; + data.health_notices.push_back({id(ds_turd_flinger_hms_1_code).state, id(ds_turd_flinger_hms_1_message).state, id(ds_turd_flinger_hms_1_severity).state}); + data.health_notices.push_back({id(ds_turd_flinger_hms_2_code).state, id(ds_turd_flinger_hms_2_message).state, id(ds_turd_flinger_hms_2_severity).state}); + data.health_notices.push_back({id(ds_turd_flinger_hms_3_code).state, id(ds_turd_flinger_hms_3_message).state, id(ds_turd_flinger_hms_3_severity).state}); const int seconds_left = bambu_display::duration_seconds(data.time_left, data.remaining_unit); const auto clock = id(ha_time).now(); if (clock.is_valid() && seconds_left >= 0) @@ -818,7 +1196,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(data, {id(ds_turd_flinger_status_label), id(ds_turd_flinger_task_label), id(ds_turd_flinger_top_progress), id(ds_turd_flinger_remaining_label), id(ds_turd_flinger_layers_label), id(ds_turd_flinger_stage_label), {id(ds_turd_flinger_progress_arc), id(ds_turd_flinger_progress_label), id(ds_turd_flinger_progress_detail)}, {id(ds_turd_flinger_nozzle_arc), id(ds_turd_flinger_nozzle_label), id(ds_turd_flinger_nozzle_detail)}, {id(ds_turd_flinger_bed_arc), id(ds_turd_flinger_bed_label), id(ds_turd_flinger_bed_detail)}, {id(ds_turd_flinger_fan_arc), id(ds_turd_flinger_fan_label), id(ds_turd_flinger_fan_detail)}, {id(ds_turd_flinger_aux_arc), id(ds_turd_flinger_aux_label), id(ds_turd_flinger_aux_detail)}, {id(ds_turd_flinger_exhaust_arc), id(ds_turd_flinger_exhaust_label), id(ds_turd_flinger_exhaust_detail)}, {{id(ds_turd_flinger_tray_0_card), id(ds_turd_flinger_tray_0_swatch), id(ds_turd_flinger_tray_0_label)}, {id(ds_turd_flinger_tray_1_card), id(ds_turd_flinger_tray_1_swatch), id(ds_turd_flinger_tray_1_label)}, {id(ds_turd_flinger_tray_2_card), id(ds_turd_flinger_tray_2_swatch), id(ds_turd_flinger_tray_2_label)}, {id(ds_turd_flinger_tray_3_card), id(ds_turd_flinger_tray_3_swatch), id(ds_turd_flinger_tray_3_label)}}}); + bambu_display::render(data, {id(ds_turd_flinger_status_label), id(ds_turd_flinger_task_label), id(ds_turd_flinger_top_progress), id(ds_turd_flinger_remaining_label), id(ds_turd_flinger_layers_label), id(ds_turd_flinger_stage_label), id(ds_turd_flinger_health_details), {id(ds_turd_flinger_progress_arc), id(ds_turd_flinger_progress_label), id(ds_turd_flinger_progress_detail)}, {id(ds_turd_flinger_nozzle_arc), id(ds_turd_flinger_nozzle_label), id(ds_turd_flinger_nozzle_detail)}, {id(ds_turd_flinger_bed_arc), id(ds_turd_flinger_bed_label), id(ds_turd_flinger_bed_detail)}, {id(ds_turd_flinger_fan_arc), id(ds_turd_flinger_fan_label), id(ds_turd_flinger_fan_detail)}, {id(ds_turd_flinger_aux_arc), id(ds_turd_flinger_aux_label), id(ds_turd_flinger_aux_detail)}, {id(ds_turd_flinger_exhaust_arc), id(ds_turd_flinger_exhaust_label), id(ds_turd_flinger_exhaust_detail)}, {{id(ds_turd_flinger_tray_0_card), id(ds_turd_flinger_tray_0_swatch), id(ds_turd_flinger_tray_0_label)}, {id(ds_turd_flinger_tray_1_card), id(ds_turd_flinger_tray_1_swatch), id(ds_turd_flinger_tray_1_label)}, {id(ds_turd_flinger_tray_2_card), id(ds_turd_flinger_tray_2_swatch), id(ds_turd_flinger_tray_2_label)}, {id(ds_turd_flinger_tray_3_card), id(ds_turd_flinger_tray_3_swatch), id(ds_turd_flinger_tray_3_label)}}}); - id: jordyns_layer_slayer_refresh then: - lambda: !lambda |- @@ -839,9 +1217,15 @@ script: data.online = id(jordyns_layer_slayer_online).state; data.print_error = id(jordyns_layer_slayer_print_error).state; data.hms_error = id(jordyns_layer_slayer_hms_error).state; + data.hms_count = id(jordyns_layer_slayer_hms_count).state; + data.print_error_message = id(jordyns_layer_slayer_print_error_message).state; + data.print_error_code = id(jordyns_layer_slayer_print_error_code).state; data.remaining_unit = id(jordyns_layer_slayer_remaining_unit).state; data.nozzle_unit = id(jordyns_layer_slayer_nozzle_unit).state; data.bed_unit = id(jordyns_layer_slayer_bed_unit).state; + data.health_notices.push_back({id(jordyns_layer_slayer_hms_1_code).state, id(jordyns_layer_slayer_hms_1_message).state, id(jordyns_layer_slayer_hms_1_severity).state}); + data.health_notices.push_back({id(jordyns_layer_slayer_hms_2_code).state, id(jordyns_layer_slayer_hms_2_message).state, id(jordyns_layer_slayer_hms_2_severity).state}); + data.health_notices.push_back({id(jordyns_layer_slayer_hms_3_code).state, id(jordyns_layer_slayer_hms_3_message).state, id(jordyns_layer_slayer_hms_3_severity).state}); const int seconds_left = bambu_display::duration_seconds(data.time_left, data.remaining_unit); const auto clock = id(ha_time).now(); if (clock.is_valid() && seconds_left >= 0) @@ -850,7 +1234,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(data, {id(jordyns_layer_slayer_status_label), id(jordyns_layer_slayer_task_label), id(jordyns_layer_slayer_top_progress), id(jordyns_layer_slayer_remaining_label), id(jordyns_layer_slayer_layers_label), id(jordyns_layer_slayer_stage_label), {id(jordyns_layer_slayer_progress_arc), id(jordyns_layer_slayer_progress_label), id(jordyns_layer_slayer_progress_detail)}, {id(jordyns_layer_slayer_nozzle_arc), id(jordyns_layer_slayer_nozzle_label), id(jordyns_layer_slayer_nozzle_detail)}, {id(jordyns_layer_slayer_bed_arc), id(jordyns_layer_slayer_bed_label), id(jordyns_layer_slayer_bed_detail)}, {id(jordyns_layer_slayer_fan_arc), id(jordyns_layer_slayer_fan_label), id(jordyns_layer_slayer_fan_detail)}, {id(jordyns_layer_slayer_aux_arc), id(jordyns_layer_slayer_aux_label), id(jordyns_layer_slayer_aux_detail)}, {id(jordyns_layer_slayer_exhaust_arc), id(jordyns_layer_slayer_exhaust_label), id(jordyns_layer_slayer_exhaust_detail)}, {{id(jordyns_layer_slayer_tray_0_card), id(jordyns_layer_slayer_tray_0_swatch), id(jordyns_layer_slayer_tray_0_label)}, {id(jordyns_layer_slayer_tray_1_card), id(jordyns_layer_slayer_tray_1_swatch), id(jordyns_layer_slayer_tray_1_label)}, {id(jordyns_layer_slayer_tray_2_card), id(jordyns_layer_slayer_tray_2_swatch), id(jordyns_layer_slayer_tray_2_label)}, {id(jordyns_layer_slayer_tray_3_card), id(jordyns_layer_slayer_tray_3_swatch), id(jordyns_layer_slayer_tray_3_label)}}}); + bambu_display::render(data, {id(jordyns_layer_slayer_status_label), id(jordyns_layer_slayer_task_label), id(jordyns_layer_slayer_top_progress), id(jordyns_layer_slayer_remaining_label), id(jordyns_layer_slayer_layers_label), id(jordyns_layer_slayer_stage_label), id(jordyns_layer_slayer_health_details), {id(jordyns_layer_slayer_progress_arc), id(jordyns_layer_slayer_progress_label), id(jordyns_layer_slayer_progress_detail)}, {id(jordyns_layer_slayer_nozzle_arc), id(jordyns_layer_slayer_nozzle_label), id(jordyns_layer_slayer_nozzle_detail)}, {id(jordyns_layer_slayer_bed_arc), id(jordyns_layer_slayer_bed_label), id(jordyns_layer_slayer_bed_detail)}, {id(jordyns_layer_slayer_fan_arc), id(jordyns_layer_slayer_fan_label), id(jordyns_layer_slayer_fan_detail)}, {id(jordyns_layer_slayer_aux_arc), id(jordyns_layer_slayer_aux_label), id(jordyns_layer_slayer_aux_detail)}, {id(jordyns_layer_slayer_exhaust_arc), id(jordyns_layer_slayer_exhaust_label), id(jordyns_layer_slayer_exhaust_detail)}, {{id(jordyns_layer_slayer_tray_0_card), id(jordyns_layer_slayer_tray_0_swatch), id(jordyns_layer_slayer_tray_0_label)}, {id(jordyns_layer_slayer_tray_1_card), id(jordyns_layer_slayer_tray_1_swatch), id(jordyns_layer_slayer_tray_1_label)}, {id(jordyns_layer_slayer_tray_2_card), id(jordyns_layer_slayer_tray_2_swatch), id(jordyns_layer_slayer_tray_2_label)}, {id(jordyns_layer_slayer_tray_3_card), id(jordyns_layer_slayer_tray_3_swatch), id(jordyns_layer_slayer_tray_3_label)}}}); - id: constipation_orion_refresh then: - lambda: !lambda |- @@ -876,9 +1260,15 @@ script: data.online = id(constipation_orion_online).state; data.print_error = id(constipation_orion_print_error).state; data.hms_error = id(constipation_orion_hms_error).state; + data.hms_count = id(constipation_orion_hms_count).state; + data.print_error_message = id(constipation_orion_print_error_message).state; + data.print_error_code = id(constipation_orion_print_error_code).state; data.remaining_unit = id(constipation_orion_remaining_unit).state; data.nozzle_unit = id(constipation_orion_nozzle_unit).state; data.bed_unit = id(constipation_orion_bed_unit).state; + data.health_notices.push_back({id(constipation_orion_hms_1_code).state, id(constipation_orion_hms_1_message).state, id(constipation_orion_hms_1_severity).state}); + data.health_notices.push_back({id(constipation_orion_hms_2_code).state, id(constipation_orion_hms_2_message).state, id(constipation_orion_hms_2_severity).state}); + data.health_notices.push_back({id(constipation_orion_hms_3_code).state, id(constipation_orion_hms_3_message).state, id(constipation_orion_hms_3_severity).state}); data.dual_nozzle = true; const int seconds_left = bambu_display::duration_seconds(data.time_left, data.remaining_unit); const auto clock = id(ha_time).now(); @@ -888,7 +1278,11 @@ script: data.trays.push_back({"A2", id(constipation_orion_tray_1_type).state, id(constipation_orion_tray_1_color).state, id(constipation_orion_tray_1_active).state}); data.trays.push_back({"A3", id(constipation_orion_tray_2_type).state, id(constipation_orion_tray_2_color).state, id(constipation_orion_tray_2_active).state}); data.trays.push_back({"A4", id(constipation_orion_tray_3_type).state, id(constipation_orion_tray_3_color).state, id(constipation_orion_tray_3_active).state}); - bambu_display::render(data, {id(constipation_orion_status_label), id(constipation_orion_task_label), id(constipation_orion_top_progress), id(constipation_orion_remaining_label), id(constipation_orion_layers_label), id(constipation_orion_stage_label), {id(constipation_orion_progress_arc), id(constipation_orion_progress_label), id(constipation_orion_progress_detail)}, {id(constipation_orion_nozzle_arc), id(constipation_orion_nozzle_label), id(constipation_orion_nozzle_detail)}, {id(constipation_orion_bed_arc), id(constipation_orion_bed_label), id(constipation_orion_bed_detail)}, {id(constipation_orion_fan_arc), id(constipation_orion_fan_label), id(constipation_orion_fan_detail)}, {id(constipation_orion_aux_arc), id(constipation_orion_aux_label), id(constipation_orion_aux_detail)}, {id(constipation_orion_exhaust_arc), id(constipation_orion_exhaust_label), id(constipation_orion_exhaust_detail)}, {{id(constipation_orion_tray_0_card), id(constipation_orion_tray_0_swatch), id(constipation_orion_tray_0_label)}, {id(constipation_orion_tray_1_card), id(constipation_orion_tray_1_swatch), id(constipation_orion_tray_1_label)}, {id(constipation_orion_tray_2_card), id(constipation_orion_tray_2_swatch), id(constipation_orion_tray_2_label)}, {id(constipation_orion_tray_3_card), id(constipation_orion_tray_3_swatch), id(constipation_orion_tray_3_label)}}}); + data.trays.push_back({"B1", id(constipation_orion_tray_4_type).state, id(constipation_orion_tray_4_color).state, id(constipation_orion_tray_4_active).state}); + 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(data, {id(constipation_orion_status_label), id(constipation_orion_task_label), id(constipation_orion_top_progress), id(constipation_orion_remaining_label), id(constipation_orion_layers_label), id(constipation_orion_stage_label), id(constipation_orion_health_details), {id(constipation_orion_progress_arc), id(constipation_orion_progress_label), id(constipation_orion_progress_detail)}, {id(constipation_orion_nozzle_arc), id(constipation_orion_nozzle_label), id(constipation_orion_nozzle_detail)}, {id(constipation_orion_bed_arc), id(constipation_orion_bed_label), id(constipation_orion_bed_detail)}, {id(constipation_orion_fan_arc), id(constipation_orion_fan_label), id(constipation_orion_fan_detail)}, {id(constipation_orion_aux_arc), id(constipation_orion_aux_label), id(constipation_orion_aux_detail)}, {id(constipation_orion_exhaust_arc), id(constipation_orion_exhaust_label), id(constipation_orion_exhaust_detail)}, {{id(constipation_orion_tray_0_card), id(constipation_orion_tray_0_swatch), id(constipation_orion_tray_0_label)}, {id(constipation_orion_tray_1_card), id(constipation_orion_tray_1_swatch), id(constipation_orion_tray_1_label)}, {id(constipation_orion_tray_2_card), id(constipation_orion_tray_2_swatch), id(constipation_orion_tray_2_label)}, {id(constipation_orion_tray_3_card), id(constipation_orion_tray_3_swatch), id(constipation_orion_tray_3_label)}, {id(constipation_orion_tray_4_card), id(constipation_orion_tray_4_swatch), id(constipation_orion_tray_4_label)}, {id(constipation_orion_tray_5_card), id(constipation_orion_tray_5_swatch), id(constipation_orion_tray_5_label)}, {id(constipation_orion_tray_6_card), id(constipation_orion_tray_6_swatch), id(constipation_orion_tray_6_label)}, {id(constipation_orion_tray_7_card), id(constipation_orion_tray_7_swatch), id(constipation_orion_tray_7_label)}}}); interval: - interval: 1s then: @@ -986,6 +1380,9 @@ lvgl: gesture_bubble: true id: prints_charming_status_label text_align: RIGHT + clickable: true + on_click: + - lvgl.widget.show: prints_charming_health_panel - obj: x: 578 y: 64 @@ -1334,10 +1731,13 @@ lvgl: height: 20 text_color: 11449271 text_font: montserrat_12 - long_mode: DOT + long_mode: SCROLL_CIRCULAR gesture_bubble: true id: prints_charming_stage_label text_align: CENTER + clickable: true + on_click: + - lvgl.widget.show: prints_charming_health_panel - label: text: -- x: 594 @@ -1740,6 +2140,68 @@ lvgl: gesture_bubble: true id: prints_charming_task_label text_align: CENTER + - obj: + id: prints_charming_health_panel + x: 24 + y: 62 + width: 752 + height: 366 + hidden: true + bg_color: 0 + bg_opa: COVER + border_width: 2 + border_color: 11449271 + radius: 10 + pad_all: 0 + scrollable: false + gesture_bubble: false + widgets: + - label: + text: Printer health / PrintsCharming + x: 16 + y: 14 + width: 716 + height: 28 + text_color: 15658734 + text_font: montserrat_20 + long_mode: DOT + gesture_bubble: true + - obj: + x: 12 + y: 50 + width: 724 + height: 246 + bg_color: 0 + border_width: 0 + pad_all: 0 + scroll_dir: VER + scrollbar_mode: AUTO + gesture_bubble: false + widgets: + - label: + text: No current health notifications. + x: 0 + y: 0 + width: 700 + height: SIZE_CONTENT + text_color: 15658734 + text_font: montserrat_18 + long_mode: WRAP + gesture_bubble: true + id: prints_charming_health_details + - button: + x: 624 + y: 310 + width: 108 + height: 40 + bg_color: 1647403 + radius: 6 + widgets: + - label: + text: CLOSE + align: CENTER + on_click: + - lvgl.widget.hide: prints_charming_health_panel - id: polly_pocket_page bg_color: 0 bg_opa: COVER @@ -1807,6 +2269,9 @@ lvgl: gesture_bubble: true id: polly_pocket_status_label text_align: RIGHT + clickable: true + on_click: + - lvgl.widget.show: polly_pocket_health_panel - obj: x: 578 y: 64 @@ -2155,10 +2620,13 @@ lvgl: height: 20 text_color: 11449271 text_font: montserrat_12 - long_mode: DOT + long_mode: SCROLL_CIRCULAR gesture_bubble: true id: polly_pocket_stage_label text_align: CENTER + clickable: true + on_click: + - lvgl.widget.show: polly_pocket_health_panel - label: text: -- x: 594 @@ -2270,6 +2738,68 @@ lvgl: gesture_bubble: true id: polly_pocket_task_label text_align: CENTER + - obj: + id: polly_pocket_health_panel + x: 24 + y: 62 + width: 752 + height: 366 + hidden: true + bg_color: 0 + bg_opa: COVER + border_width: 2 + border_color: 11449271 + radius: 10 + pad_all: 0 + scrollable: false + gesture_bubble: false + widgets: + - label: + text: Printer health / Polly-Pocket + x: 16 + y: 14 + width: 716 + height: 28 + text_color: 15658734 + text_font: montserrat_20 + long_mode: DOT + gesture_bubble: true + - obj: + x: 12 + y: 50 + width: 724 + height: 246 + bg_color: 0 + border_width: 0 + pad_all: 0 + scroll_dir: VER + scrollbar_mode: AUTO + gesture_bubble: false + widgets: + - label: + text: No current health notifications. + x: 0 + y: 0 + width: 700 + height: SIZE_CONTENT + text_color: 15658734 + text_font: montserrat_18 + long_mode: WRAP + gesture_bubble: true + id: polly_pocket_health_details + - button: + x: 624 + y: 310 + width: 108 + height: 40 + bg_color: 1647403 + radius: 6 + widgets: + - label: + text: CLOSE + align: CENTER + on_click: + - lvgl.widget.hide: polly_pocket_health_panel - id: ds_turd_flinger_page bg_color: 0 bg_opa: COVER @@ -2337,6 +2867,9 @@ lvgl: gesture_bubble: true id: ds_turd_flinger_status_label text_align: RIGHT + clickable: true + on_click: + - lvgl.widget.show: ds_turd_flinger_health_panel - obj: x: 578 y: 64 @@ -2685,10 +3218,13 @@ lvgl: height: 20 text_color: 11449271 text_font: montserrat_12 - long_mode: DOT + long_mode: SCROLL_CIRCULAR gesture_bubble: true id: ds_turd_flinger_stage_label text_align: CENTER + clickable: true + on_click: + - lvgl.widget.show: ds_turd_flinger_health_panel - label: text: -- x: 594 @@ -2920,6 +3456,68 @@ lvgl: gesture_bubble: true id: ds_turd_flinger_task_label text_align: CENTER + - obj: + id: ds_turd_flinger_health_panel + x: 24 + y: 62 + width: 752 + height: 366 + hidden: true + bg_color: 0 + bg_opa: COVER + border_width: 2 + border_color: 11449271 + radius: 10 + pad_all: 0 + scrollable: false + gesture_bubble: false + widgets: + - label: + text: Printer health / Ds-Turd-Flinger + x: 16 + y: 14 + width: 716 + height: 28 + text_color: 15658734 + text_font: montserrat_20 + long_mode: DOT + gesture_bubble: true + - obj: + x: 12 + y: 50 + width: 724 + height: 246 + bg_color: 0 + border_width: 0 + pad_all: 0 + scroll_dir: VER + scrollbar_mode: AUTO + gesture_bubble: false + widgets: + - label: + text: No current health notifications. + x: 0 + y: 0 + width: 700 + height: SIZE_CONTENT + text_color: 15658734 + text_font: montserrat_18 + long_mode: WRAP + gesture_bubble: true + id: ds_turd_flinger_health_details + - button: + x: 624 + y: 310 + width: 108 + height: 40 + bg_color: 1647403 + radius: 6 + widgets: + - label: + text: CLOSE + align: CENTER + on_click: + - lvgl.widget.hide: ds_turd_flinger_health_panel - id: jordyns_layer_slayer_page bg_color: 0 bg_opa: COVER @@ -2987,6 +3585,9 @@ lvgl: gesture_bubble: true id: jordyns_layer_slayer_status_label text_align: RIGHT + clickable: true + on_click: + - lvgl.widget.show: jordyns_layer_slayer_health_panel - obj: x: 578 y: 64 @@ -3335,10 +3936,13 @@ lvgl: height: 20 text_color: 11449271 text_font: montserrat_12 - long_mode: DOT + long_mode: SCROLL_CIRCULAR gesture_bubble: true id: jordyns_layer_slayer_stage_label text_align: CENTER + clickable: true + on_click: + - lvgl.widget.show: jordyns_layer_slayer_health_panel - label: text: -- x: 594 @@ -3570,6 +4174,68 @@ lvgl: gesture_bubble: true id: jordyns_layer_slayer_task_label text_align: CENTER + - obj: + id: jordyns_layer_slayer_health_panel + x: 24 + y: 62 + width: 752 + height: 366 + hidden: true + bg_color: 0 + bg_opa: COVER + border_width: 2 + border_color: 11449271 + radius: 10 + pad_all: 0 + scrollable: false + gesture_bubble: false + widgets: + - label: + text: Printer health / Jordyns-Layer-Slayer + x: 16 + y: 14 + width: 716 + height: 28 + text_color: 15658734 + text_font: montserrat_20 + long_mode: DOT + gesture_bubble: true + - obj: + x: 12 + y: 50 + width: 724 + height: 246 + bg_color: 0 + border_width: 0 + pad_all: 0 + scroll_dir: VER + scrollbar_mode: AUTO + gesture_bubble: false + widgets: + - label: + text: No current health notifications. + x: 0 + y: 0 + width: 700 + height: SIZE_CONTENT + text_color: 15658734 + text_font: montserrat_18 + long_mode: WRAP + gesture_bubble: true + id: jordyns_layer_slayer_health_details + - button: + x: 624 + y: 310 + width: 108 + height: 40 + bg_color: 1647403 + radius: 6 + widgets: + - label: + text: CLOSE + align: CENTER + on_click: + - lvgl.widget.hide: jordyns_layer_slayer_health_panel - id: constipation_orion_page bg_color: 0 bg_opa: COVER @@ -3637,6 +4303,9 @@ lvgl: gesture_bubble: true id: constipation_orion_status_label text_align: RIGHT + clickable: true + on_click: + - lvgl.widget.show: constipation_orion_health_panel - obj: x: 578 y: 64 @@ -3985,10 +4654,13 @@ lvgl: height: 20 text_color: 11449271 text_font: montserrat_12 - long_mode: DOT + long_mode: SCROLL_CIRCULAR gesture_bubble: true id: constipation_orion_stage_label text_align: CENTER + clickable: true + on_click: + - lvgl.widget.show: constipation_orion_health_panel - label: text: -- x: 594 @@ -4172,6 +4844,177 @@ lvgl: gesture_bubble: true id: constipation_orion_tray_3_label text_align: CENTER + - label: + text: AMS B + x: 594 + y: 215 + width: 190 + height: 28 + text_color: 11449271 + text_font: montserrat_20 + long_mode: DOT + gesture_bubble: true + text_align: CENTER + - obj: + id: constipation_orion_tray_4_card + x: 594 + y: 247 + width: 44 + height: 82 + bg_color: 0 + radius: 5 + border_width: 1 + border_color: 1647403 + pad_all: 0 + scrollable: false + gesture_bubble: true + widgets: + - obj: + id: constipation_orion_tray_4_swatch + x: 3 + y: 3 + width: 36 + height: 40 + bg_color: 1647403 + radius: 4 + border_width: 0 + pad_all: 0 + scrollable: false + gesture_bubble: true + - label: + text: 'B1 + + --' + x: 1 + y: 46 + width: 40 + height: 34 + text_color: 15658734 + text_font: montserrat_12 + long_mode: DOT + gesture_bubble: true + id: constipation_orion_tray_4_label + text_align: CENTER + - obj: + id: constipation_orion_tray_5_card + x: 642 + y: 247 + width: 44 + height: 82 + bg_color: 0 + radius: 5 + border_width: 1 + border_color: 1647403 + pad_all: 0 + scrollable: false + gesture_bubble: true + widgets: + - obj: + id: constipation_orion_tray_5_swatch + x: 3 + y: 3 + width: 36 + height: 40 + bg_color: 1647403 + radius: 4 + border_width: 0 + pad_all: 0 + scrollable: false + gesture_bubble: true + - label: + text: 'B2 + + --' + x: 1 + y: 46 + width: 40 + height: 34 + text_color: 15658734 + text_font: montserrat_12 + long_mode: DOT + gesture_bubble: true + id: constipation_orion_tray_5_label + text_align: CENTER + - obj: + id: constipation_orion_tray_6_card + x: 690 + y: 247 + width: 44 + height: 82 + bg_color: 0 + radius: 5 + border_width: 1 + border_color: 1647403 + pad_all: 0 + scrollable: false + gesture_bubble: true + widgets: + - obj: + id: constipation_orion_tray_6_swatch + x: 3 + y: 3 + width: 36 + height: 40 + bg_color: 1647403 + radius: 4 + border_width: 0 + pad_all: 0 + scrollable: false + gesture_bubble: true + - label: + text: 'B3 + + --' + x: 1 + y: 46 + width: 40 + height: 34 + text_color: 15658734 + text_font: montserrat_12 + long_mode: DOT + gesture_bubble: true + id: constipation_orion_tray_6_label + text_align: CENTER + - obj: + id: constipation_orion_tray_7_card + x: 738 + y: 247 + width: 44 + height: 82 + bg_color: 0 + radius: 5 + border_width: 1 + border_color: 1647403 + pad_all: 0 + scrollable: false + gesture_bubble: true + widgets: + - obj: + id: constipation_orion_tray_7_swatch + x: 3 + y: 3 + width: 36 + height: 40 + bg_color: 1647403 + radius: 4 + border_width: 0 + pad_all: 0 + scrollable: false + gesture_bubble: true + - label: + text: 'B4 + + --' + x: 1 + y: 46 + width: 40 + height: 34 + text_color: 15658734 + text_font: montserrat_12 + long_mode: DOT + gesture_bubble: true + id: constipation_orion_tray_7_label + text_align: CENTER - button: x: 16 y: 446 @@ -4220,6 +5063,68 @@ lvgl: gesture_bubble: true id: constipation_orion_task_label text_align: CENTER + - obj: + id: constipation_orion_health_panel + x: 24 + y: 62 + width: 752 + height: 366 + hidden: true + bg_color: 0 + bg_opa: COVER + border_width: 2 + border_color: 11449271 + radius: 10 + pad_all: 0 + scrollable: false + gesture_bubble: false + widgets: + - label: + text: Printer health / Constipation-Orion + x: 16 + y: 14 + width: 716 + height: 28 + text_color: 15658734 + text_font: montserrat_20 + long_mode: DOT + gesture_bubble: true + - obj: + x: 12 + y: 50 + width: 724 + height: 246 + bg_color: 0 + border_width: 0 + pad_all: 0 + scroll_dir: VER + scrollbar_mode: AUTO + gesture_bubble: false + widgets: + - label: + text: No current health notifications. + x: 0 + y: 0 + width: 700 + height: SIZE_CONTENT + text_color: 15658734 + text_font: montserrat_18 + long_mode: WRAP + gesture_bubble: true + id: constipation_orion_health_details + - button: + x: 624 + y: 310 + width: 108 + height: 40 + bg_color: 1647403 + radius: 6 + widgets: + - label: + text: CLOSE + align: CENTER + on_click: + - lvgl.widget.hide: constipation_orion_health_panel - id: color_test_page skip: true bg_color: 0 diff --git a/esphome/bambu-display/preview.py b/esphome/bambu-display/preview.py index d17975d..01b32e0 100644 --- a/esphome/bambu-display/preview.py +++ b/esphome/bambu-display/preview.py @@ -25,6 +25,7 @@ def main(): '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', + 'hms_count': '0', 'print_error_message': '', 'print_error_code': '', 'remaining_unit': 'h', 'nozzle_unit': '°C', 'bed_unit': '°C'} colors = ['#60DE9AFF', '#F4C96BFF', '#74A9E8FF', '#F8F4ECFF', '#DF8BBFFF', '#FA936BFF', '#8E79CAFF', '#4CC9C1FF'] @@ -42,6 +43,8 @@ def main(): _, index, attribute = key.split('_') text = {'type': 'PLA', 'color': colors[int(index) % len(colors)], 'active': 'on' if index == '0' else 'off'}[attribute] + elif key.startswith(('hms_1_', 'hms_2_', 'hms_3_')): + text = '' else: text = demo_text[key] value = 'std::string(' + json.dumps(text, ensure_ascii=False) + ')' @@ -85,8 +88,26 @@ inline void setup() { {'snapshot.take': {'id': 'panel_display', 'filename': printer['id'] + '.bmp'}}] steps += [{'lvgl.page.show': 'color_test_page'}, {'delay': '300ms'}, {'snapshot.take': {'id': 'panel_display', 'filename': 'color_test.bmp'}}] + demo_printer = printers[0]['id'] + for key, value in {'hms_error': 'on', 'hms_count': '1', 'hms_1_code': 'DEMO_CODE', + 'hms_1_severity': 'common', + 'hms_1_message': 'DEMO: A printer maintenance notification. This is synthetic preview data, not an actual fault diagnosis.'}.items(): + steps.append({'text_sensor.template.publish': {'id': f'{demo_printer}_{key}', 'state': value}}) + steps += [{'lvgl.page.show': demo_printer + '_page'}, + {'script.execute': demo_printer + '_refresh'}, + {'lvgl.widget.show': demo_printer + '_health_panel'}, {'delay': '300ms'}, + {'snapshot.take': {'id': 'panel_display', 'filename': 'health_notice.bmp'}}, + {'lvgl.widget.hide': demo_printer + '_health_panel'}, + {'text_sensor.template.publish': {'id': demo_printer + '_hms_error', 'state': 'off'}}, + {'script.execute': demo_printer + '_refresh'}] steps += [{'lvgl.page.show': printers[0]['id'] + '_page'}, {'delay': '250ms'}, {'lambda': Lambda('bambu_preview::setup();')}] + for x, y, expect_hidden in [(690, 30, False), (700, 394, True)]: + steps += [ + {'lambda': Lambda(f'bambu_preview::x = {x}; bambu_preview::y = {y}; bambu_preview::pressed = true;')}, + {'delay': '70ms'}, {'lambda': Lambda('bambu_preview::pressed = false;')}, {'delay': '150ms'}, + {'lambda': Lambda(f'if (lv_obj_has_flag(id({demo_printer}_health_panel), LV_OBJ_FLAG_HIDDEN) != {str(expect_hidden).lower()}) {{ ESP_LOGE("preview", "Health panel tap failed"); exit(3); }}')}, + ] if len(printers) > 1: # First -> second -> first -> last -> first, then a swipe starting # on NEXT must advance exactly once even when released over PREV. @@ -119,12 +140,13 @@ inline void setup() { 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 name in [printer['id'] for printer in printers] + ['color_test']: + for name in [printer['id'] for printer in printers] + ['color_test', 'health_notice']: bmp = root / '.esphome/snapshots/bambu-display-preview' / (name + '.bmp') with Image.open(bmp) as image: image.save(args.output / (name + '.png')) print(f'Saved {len(printers)} demo screenshots to {args.output}') print('Passed LVGL pointer swipe tests, including wraparound and button gesture consumption.') + print('Passed status-tap health panel open and Close-button tests.') if __name__ == '__main__': diff --git a/esphome/bambu-display/previews/color_test.png b/esphome/bambu-display/previews/color_test.png new file mode 100644 index 0000000..9b93cde Binary files /dev/null and b/esphome/bambu-display/previews/color_test.png differ diff --git a/esphome/bambu-display/previews/constipation_orion.png b/esphome/bambu-display/previews/constipation_orion.png index de7dd06..af5d046 100644 Binary files a/esphome/bambu-display/previews/constipation_orion.png and b/esphome/bambu-display/previews/constipation_orion.png differ diff --git a/esphome/bambu-display/previews/ds_turd_flinger.png b/esphome/bambu-display/previews/ds_turd_flinger.png index 01f66e3..60de212 100644 Binary files a/esphome/bambu-display/previews/ds_turd_flinger.png and b/esphome/bambu-display/previews/ds_turd_flinger.png differ diff --git a/esphome/bambu-display/previews/health_notice.png b/esphome/bambu-display/previews/health_notice.png new file mode 100644 index 0000000..bca6f54 Binary files /dev/null and b/esphome/bambu-display/previews/health_notice.png differ diff --git a/esphome/bambu-display/previews/jordyns_layer_slayer.png b/esphome/bambu-display/previews/jordyns_layer_slayer.png index a637c28..b73c4d8 100644 Binary files a/esphome/bambu-display/previews/jordyns_layer_slayer.png and b/esphome/bambu-display/previews/jordyns_layer_slayer.png differ diff --git a/esphome/bambu-display/previews/polly_pocket.png b/esphome/bambu-display/previews/polly_pocket.png index 37607ea..b586bbf 100644 Binary files a/esphome/bambu-display/previews/polly_pocket.png and b/esphome/bambu-display/previews/polly_pocket.png differ diff --git a/esphome/bambu-display/previews/prints_charming.png b/esphome/bambu-display/previews/prints_charming.png index 18c953c..a73c508 100644 Binary files a/esphome/bambu-display/previews/prints_charming.png and b/esphome/bambu-display/previews/prints_charming.png differ diff --git a/esphome/bambu-display/printers.yaml b/esphome/bambu-display/printers.yaml index b22ad07..4c59b56 100644 --- a/esphome/bambu-display/printers.yaml +++ b/esphome/bambu-display/printers.yaml @@ -46,3 +46,6 @@ printers: filament_banks: - label: A entity_prefix: constipation_orion_ams_1 + - label: B + # This unit retains its original hardware-based HA entity names. + entity_prefix: h2c_31b8ap5a2200288_ams_2 diff --git a/esphome/bambu-display/test_dashboard.cpp b/esphome/bambu-display/test_dashboard.cpp index bc0f854..37c0417 100644 --- a/esphome/bambu-display/test_dashboard.cpp +++ b/esphome/bambu-display/test_dashboard.cpp @@ -50,9 +50,25 @@ int main() { assert(data.status_color() == RED); data.print_error = "off"; data.hms_error = "True"; - assert(data.status_text() == "HMS ALERT"); + assert(data.status_text() == "HEALTH NOTICE"); + assert(data.status_color() == AMBER); + assert(data.health_details().find("have not arrived") != std::string::npos); + data.hms_count = "1"; + data.health_notices = {{"DEMO_CODE", "A synthetic information message", "info"}}; + assert(data.status_text() == "HEALTH INFO"); + assert(data.health_details().find("DEMO_CODE") != std::string::npos); + assert(data.alert_summary() == "A synthetic information message"); + data.health_notices.push_back({"DEMO_2", "A synthetic serious message", "serious"}); + data.hms_count = "2"; + assert(data.status_text() == "HEALTH ERROR"); assert(data.status_color() == RED); + assert(data.health_details().find("A synthetic serious message") != std::string::npos); + data.health_notices.pop_back(); + data.hms_count = "5"; + assert(data.status_text() == "HEALTH NOTICE"); + assert(data.health_details().find("4 more") != std::string::npos); data.hms_error = "off"; + assert(data.health_details() == "No current printer health notifications."); data.status = "finish"; assert(data.status_text() == "COMPLETE"); assert(data.status_color() == GREEN); diff --git a/esphome/bambu-display/test_generate.py b/esphome/bambu-display/test_generate.py index 2d345fc..c88ed25 100644 --- a/esphome/bambu-display/test_generate.py +++ b/esphome/bambu-display/test_generate.py @@ -41,6 +41,22 @@ class FleetTests(unittest.TestCase): entry = next(row for row in package[domain] if row['id'] == f'prints_charming_{key}') self.assertEqual(entry['entity_id'], 'sensor.custom_remaining') + def test_orion_has_both_ams_units_with_their_actual_entity_prefixes(self): + package = generate(self.printers) + types = [entry['entity_id'] for entry in package['text_sensor'] + if entry['id'].startswith('constipation_orion_tray_') and entry.get('attribute') == 'type'] + self.assertEqual(len(types), 8) + self.assertIn('sensor.constipation_orion_ams_1_tray_1', types) + self.assertIn('sensor.h2c_31b8ap5a2200288_ams_2_tray_4', types) + + def test_health_messages_import_numbered_ha_attributes(self): + package = generate(self.printers) + subscriptions = {entry['id']: entry for entry in package['text_sensor']} + message = subscriptions['prints_charming_hms_1_message'] + self.assertEqual(message['entity_id'], 'binary_sensor.p1s_01p09c470102673_hms_errors') + self.assertEqual(message['attribute'], '1-Error') + self.assertEqual(subscriptions['prints_charming_hms_3_severity']['attribute'], '3-Severity') + def test_reject_empty_duplicate_and_malformed_printer_ids(self): for invalid in ([], self.printers + [self.printers[0]], [{**self.printers[0], 'id': 'invalid-id'}]):