diff --git a/automations.yaml b/automations.yaml index 0c04923a..f73df99f 100644 --- a/automations.yaml +++ b/automations.yaml @@ -4776,11 +4776,12 @@ - id: '1791477238799' alias: SparkX i7 - Filament sufficiency check at print start description: >- - When a print starts and stays printing for 45 s, compares the loaded CFS Lite - spools against the slicer's filament requirement. A single-material job is - checked exactly against its reported weight; a multi-material job has no - per-colour figure, so each loaded spool is compared against the whole job - total (worst case). Always sends a notification. + When a print starts and stays printing for 45 s, reports how much filament the + job needs, how much has already been used and how much is left, then compares + the loaded CFS Lite spools against that. A single-colour job whose colour is in + a loaded slot is checked exactly against that slot's spool; otherwise the + slicer gives no per-colour figure, so each loaded spool is compared against the + whole job total. Always sends a notification. triggers: - trigger: state entity_id: sensor.sparkx_i7_print_status @@ -4793,49 +4794,75 @@ length_cm: "{{ states('sensor.sparkx_i7_expected_material_length') | float(0) }}" weight_g: "{{ states('sensor.sparkx_i7_expected_material_weight') | float(0) }}" job_g: "{{ ((weight_g | float) if (weight_g | float) > 0 else (length_cm | float) * (gcm | float)) | round(1) }}" - gcol: "{{ state_attr('sensor.sparkx_i7_expected_material_length', 'color') | default('', true) | string | replace('#', '') | lower }}" - mono: "{{ (weight_g | float) > 0 and (gcol | length) <= 6 }}" + used_len: "{{ states('sensor.sparkx_i7_used_material_length') | float(0) }}" + used_g: "{{ (((used_len | float) * (gcm | float)) | round(1)) }}" + gcode_file: "{{ state_attr('sensor.sparkx_i7_expected_material_length', 'gcode_file') | default('', true) }}" - variables: report: |- - {% set ns = namespace(req=[], short=[], missing=[], loaded=[], avail=0) %} {% set cols = namespace(list=[]) %} {% for n in ['1','2','3','4'] %}{% set cc = states('sensor.sparkx_i7_cfs_box_1_slot_' ~ n ~ '_color') | replace('#','') | lower %}{% if cc not in ['','none','unknown','unavailable'] %}{% set cols.list = cols.list + [cc] %}{% endif %}{% endfor %} - {% set per_slot = gcol if (mono and gcol != '' and gcol in cols.list) else '' %} - {% set mono_offline = mono and gcol != '' and gcol not in cols.list %} + {% set gc = namespace(list=[]) %} + {% for p in (state_attr('sensor.sparkx_i7_expected_material_length','color') | default('', true) | string).split(';') %}{% set q = p | replace('#','') | lower | trim %}{% if q not in ['','none','unknown','unavailable'] %}{% set gc.list = gc.list + [q] %}{% endif %}{% endfor %} + {% set mc = namespace(list=[]) %} + {% for p in (state_attr('sensor.sparkx_i7_expected_material_length','material') | default('', true) | string).split(';') %}{% set q = p | trim %}{% if q not in ['','none','unknown','unavailable'] %}{% set mc.list = mc.list + [q] %}{% endif %}{% endfor %} + {% set single = (gc.list | length) == 1 %} + {% set gcol = gc.list[0] if single else '' %} + {% set rem_g = ([((job_g | float) - (used_g | float)), 0] | max) | round(1) %} + {% set ns = namespace(cover=[], below=[], short=[], ok=[], missing=[], loaded=[], match_label='', match_rem=0) %} {% for n in ['1','2','3','4'] %} {% set c = states('sensor.sparkx_i7_cfs_box_1_slot_' ~ n ~ '_color') | replace('#','') | lower %} {% if c not in ['','none','unknown','unavailable'] %} {% set cname = states('sensor.sparkx_i7_slot_' ~ n ~ '_color_name') %} {% set flabel = states('sensor.sparkx_i7_cfs_box_1_slot_' ~ n ~ '_filament') %} {% set label = 'Slot ' ~ n ~ ' ' ~ (cname if cname not in ['','unknown','unavailable'] else (flabel if flabel not in ['','unknown','unavailable'] else ('#' ~ (c | upper)))) %} - {% set sp = namespace(id='', rem=0) %} + {% set sp = namespace(id='', rem=0, fw=0) %} {% for e in integration_entities('spoolman') if e.endswith('_color_hex') %} {% if sp.id == '' and (states(e) | replace('#','') | lower) == c and (state_attr(e[:-10],'location') | default('', true) | lower) == 'cfs lite' %} {% set sp.id = e[:-10] %} {% set sp.rem = states(e[:-10]) | float(0) %} + {% set sp.fw = state_attr(e[:-10],'filament_weight') | float(0) %} {% endif %} {% endfor %} - {% if sp.id != '' %}{% set ns.avail = ns.avail + sp.rem %}{% set ns.loaded = ns.loaded + [label ~ ': ' ~ (sp.rem | round(1)) ~ ' g'] %}{% endif %} - {% if job_g > 0 %} - {% set req = job_g if (per_slot == '' or c == per_slot) else 0 %} - {% if req > 0 %} - {% if sp.id == '' %}{% set ns.missing = ns.missing + [label ~ ' (#' ~ (c | upper) ~ ')'] %} - {% elif sp.rem < req %}{% set ns.short = ns.short + [label ~ ': ' ~ (sp.rem | round(1)) ~ ' g left, up to ' ~ req ~ ' g needed'] %} - {% else %}{% set ns.req = ns.req + [label ~ ': ' ~ (sp.rem | round(1)) ~ ' g left, up to ' ~ req ~ ' g needed'] %}{% endif %} + {% set pct = ((((sp.rem | float) / (sp.fw | float)) * 100) | round(0) | int) if (sp.fw | float) > 0 else 0 %} + {% if sp.id != '' %}{% set ns.loaded = ns.loaded + [label ~ ': ' ~ (sp.rem | round(1)) ~ ' g (' ~ pct ~ '%)'] %}{% endif %} + {% if sp.id != '' and single and c == gcol %}{% set ns.match_label = label %}{% set ns.match_rem = sp.rem %}{% endif %} + {% if sp.id != '' %} + {% if single and c == gcol %} + {% if (sp.rem | float) < (job_g | float) %}{% set ns.short = ns.short + [label ~ ': ' ~ (sp.rem | round(1)) ~ ' g (' ~ pct ~ '%) left, ' ~ job_g ~ ' g needed'] %} + {% else %}{% set ns.ok = ns.ok + [label ~ ': ' ~ (sp.rem | round(1)) ~ ' g (' ~ pct ~ '%) left, ' ~ job_g ~ ' g needed'] %}{% endif %} + {% elif single %} + {% if (sp.rem | float) >= (job_g | float) %}{% set ns.cover = ns.cover + [label ~ ': ' ~ (sp.rem | round(1)) ~ ' g (' ~ pct ~ '%)'] %} + {% else %}{% set ns.below = ns.below + [label ~ ': ' ~ (sp.rem | round(1)) ~ ' g (' ~ pct ~ '%)'] %}{% endif %} + {% else %} + {% if (sp.rem | float) < (job_g | float) %}{% set ns.short = ns.short + [label ~ ': ' ~ (sp.rem | round(1)) ~ ' g (' ~ pct ~ '%) left, up to ' ~ job_g ~ ' g needed'] %} + {% else %}{% set ns.ok = ns.ok + [label ~ ': ' ~ (sp.rem | round(1)) ~ ' g (' ~ pct ~ '%) left, up to ' ~ job_g ~ ' g needed'] %}{% endif %} {% endif %} + {% elif job_g > 0 %} + {% set ns.missing = ns.missing + [label ~ ' (#' ~ (c | upper) ~ ')'] %} {% endif %} {% endif %} {% endfor %} + Job: {{ gcode_file if gcode_file not in ['','unknown','unavailable'] else (state_attr('sensor.sparkx_i7_print_status','file') | default('', true) | string).split('/')[-1] }} + Needs {{ job_g | round(1) }} g ({{ length_cm | round(1) }} cm) · used {{ used_g }} g · {{ rem_g }} g left to print + Slicer: {{ (mc.list | join(', ')) if mc.list else 'material unknown' }} · {{ ('single colour #' ~ (gcol | upper)) if single else ((gc.list | length) ~ ' colours') }} {% if job_g <= 0 %}Cannot verify: slicer filament estimate not available yet.{% if ns.loaded %} Loaded: {{ ns.loaded | join('; ') }}.{% endif %} - {% elif mono_offline %}Cannot verify: this looks like a single-colour print of #{{ gcol | upper }}, which is not in a loaded CFS slot. + {% elif single and gcol in cols.list %} + {% if ns.short %}NOT ENOUGH filament: + {{ ns.short | join('\n') }} + {% elif ns.match_label %}Enough filament: {{ ns.match_label }}: {{ ns.match_rem | round(1) }} g left, {{ job_g }} g needed. + {% else %}Matched slot {{ gcol | upper }} has no Spoolman spool at CFS Lite, so remaining cannot be checked.{% endif %} + {% elif single %}No exact colour match for the print colour #{{ gcol | upper }} in a loaded CFS slot, so the exact spool cannot be identified.{% if ns.cover %} + Would cover {{ job_g }} g: {{ ns.cover | join('; ') }}{% endif %}{% if ns.below %} + Below {{ job_g }} g: {{ ns.below | join('; ') }}{% endif %}{% if not ns.cover and not ns.below %} + No loaded CFS slot has a matching Spoolman spool to compare.{% endif %} {% elif ns.short %}NOT ENOUGH filament - possible shortfall: - {{ ns.short | join('\n') }}{% if ns.req %} - OK: {{ ns.req | join('; ') }}{% endif %}{% if ns.missing %} + {{ ns.short | join('\n') }}{% if ns.ok %} + OK (would still cover the whole job): {{ ns.ok | join('; ') }}{% endif %}{% if ns.missing %} No Spoolman spool matched for: {{ ns.missing | join(', ') }}{% endif %} - {% elif ns.req %}Enough filament: - {{ ns.req | join('\n') }}{% if ns.missing %} + {% elif ns.ok %}Enough filament: + {{ ns.ok | join('\n') }}{% if ns.missing %} No Spoolman spool matched for: {{ ns.missing | join(', ') }}{% endif %} - {% else %}Cannot verify: could not match this job's filament to a loaded CFS slot.{% endif %} + {% elif ns.missing %}Could not match any loaded CFS slot to a Spoolman spool.{% endif %} - action: notify.persistent_notification data: title: Print started - filament check