From 1b7ae00ddb003cc9e0cfcb0d408bee34f46c2fca Mon Sep 17 00:00:00 2001 From: Home Assistant Version Control Date: Thu, 8 Oct 2026 17:45:13 +0000 Subject: [PATCH] 3 files --- ...nt_spool_for_spoolman.20261008_174506.yaml | 53 +++++++ .ha_mcp/logs/mcp_usage.jsonl | 1 + automations.yaml | 143 +++++++++++++++--- 3 files changed, 174 insertions(+), 23 deletions(-) create mode 100644 .ha_mcp/backups/automation.automation.sparkx_i7_latch_print_spool_for_spoolman.20261008_174506.yaml diff --git a/.ha_mcp/backups/automation.automation.sparkx_i7_latch_print_spool_for_spoolman.20261008_174506.yaml b/.ha_mcp/backups/automation.automation.sparkx_i7_latch_print_spool_for_spoolman.20261008_174506.yaml new file mode 100644 index 00000000..40d9e9a5 --- /dev/null +++ b/.ha_mcp/backups/automation.automation.sparkx_i7_latch_print_spool_for_spoolman.20261008_174506.yaml @@ -0,0 +1,53 @@ +# ha_mcp_backup +schema_version: 1 +domain: automation +entity_id: automation.sparkx_i7_latch_print_spool_for_spoolman +captured: '2026-10-08T17:45:06.877265+00:00' +tool: ha_config_set_automation +config: + id: '1791477238762' + alias: SparkX i7 - Latch print spool for Spoolman + triggers: + - trigger: state + entity_id: sensor.sparkx_i7_print_status + to: printing + id: print_started + - trigger: state + entity_id: sensor.sparkx_i7_active_filament_slot + - trigger: state + entity_id: + - sensor.sparkx_i7_cfs_box_1_slot_1_color + - sensor.sparkx_i7_cfs_box_1_slot_2_color + - sensor.sparkx_i7_cfs_box_1_slot_3_color + - sensor.sparkx_i7_cfs_box_1_slot_4_color + conditions: + - condition: state + entity_id: sensor.sparkx_i7_print_status + state: printing + actions: + - choose: + - conditions: + condition: trigger + id: print_started + sequence: + - action: input_text.set_value + target: + entity_id: input_text.sparkx_i7_print_spool_latch + data: + value: '' + - variables: + spool_id: "{%- set slot = states('sensor.sparkx_i7_active_filament_slot') -%}\n\ + {%- set ce = 'sensor.sparkx_i7_cfs_' ~ (slot | lower | replace(' ', '_'))\ + \ ~ '_color' -%}\n{%- set color = states(ce) | replace('#', '') | lower -%}\n\ + {%- set ns = namespace(id = '') -%}\n{%- for e in integration_entities('spoolman')\ + \ -%}\n {%- if e.endswith('_color_hex') and (states(e) | lower) == color\ + \ -%}\n {%- set ns.id = states(e[:-10] ~ '_id') -%}\n {%- endif -%}\n\ + {%- endfor -%}\n{{ ns.id }}" + - condition: template + value_template: '{{ spool_id not in ['''', ''unknown'', ''unavailable''] }}' + - action: input_text.set_value + target: + entity_id: input_text.sparkx_i7_print_spool_latch + data: + value: '{{ spool_id }}' + mode: single diff --git a/.ha_mcp/logs/mcp_usage.jsonl b/.ha_mcp/logs/mcp_usage.jsonl index 6b8734af..978b3390 100644 --- a/.ha_mcp/logs/mcp_usage.jsonl +++ b/.ha_mcp/logs/mcp_usage.jsonl @@ -4020,3 +4020,4 @@ {"timestamp": "2026-10-08T17:39:43.650141+00:00", "tool_name": "ha_search", "parameters": {"query": "spoolman", "domain_filter": "sensor", "area_filter": null, "search_types": null, "limit": 50, "offset": 0, "exact_match": true, "include_hidden": true, "include_config": false, "group_by_domain": false, "per_domain_limit": null, "state_filter": null, "result_fields": null, "fields": null, "config_time_budget": null, "ctx": {"_STATE_TTL_SECONDS": 86400}}, "execution_time_ms": 79.60057258605957, "success": true, "error_message": null, "response_size_bytes": 9659, "user_context": null} {"timestamp": "2026-10-08T17:39:55.643578+00:00", "tool_name": "ha_search", "parameters": {"query": "spoolman", "domain_filter": "sensor", "area_filter": null, "search_types": null, "limit": 50, "offset": 50, "exact_match": true, "include_hidden": true, "include_config": false, "group_by_domain": false, "per_domain_limit": null, "state_filter": null, "result_fields": null, "fields": null, "config_time_budget": null, "ctx": {"_STATE_TTL_SECONDS": 86400}}, "execution_time_ms": 282.75513648986816, "success": true, "error_message": null, "response_size_bytes": 5579, "user_context": null} {"timestamp": "2026-10-08T17:44:05.639322+00:00", "tool_name": "ha_config_get_automation", "parameters": {"identifier": "automation.sparkx_i7_latch_print_spool_for_spoolman"}, "execution_time_ms": 777.6463031768799, "success": true, "error_message": null, "response_size_bytes": 1740, "user_context": null} +{"timestamp": "2026-10-08T17:45:09.204304+00:00", "tool_name": "ha_config_set_automation", "parameters": {"config": null, "identifier": "automation.sparkx_i7_latch_print_spool_for_spoolman", "python_transform": "msg = \"Segment of {{ grams }} g from {{ old_slot }} (color #{{ color | upper }}) could not be deducted - no Spoolman spool with remaining weight matches that color. Register the new roll in Spoolman, then adjust manually if needed.\"\nnt = [{'action': 'notify.persistent_notification', 'data': {'title': 'Spoolman: no spool for slot color', 'message': msg}}, {'action': 'notify.mobile_app_francos_ipad', 'data': {'title': 'Spoolman: no spool for slot color', 'message': msg}}, {'action': 'notify.mobile_app_sm_f956w', 'data': {'title': 'Spoolman: no spool for slot color', 'message': msg}}]\nconfig['description'] = 'Segment tracking for Spoolman: at print start resets the length checkpoint and per-slot gram counters and latches the active CFS slot. On every slot change mid-print, deducts the exact extrusion of the finished segment (used_material_length delta x g/cm) from the Spoolman spool matching the previous slot color hex (only if that spool has remaining weight), accumulates grams per slot, notifies on no-match, then re-baselines the checkpoint and advances the latch.'\nconfig['triggers'] = [{'trigger': 'state', 'entity_id': 'sensor.sparkx_i7_print_status', 'to': 'printing', 'id': 'print_started'}, {'trigger': 'state', 'entity_id': 'sensor.sparkx_i7_active_filament_slot', 'id': 'slot_changed'}]\nconfig['conditions'] = [{'condition': 'state', 'entity_id': 'sensor.sparkx_i7_print_status', 'state': 'printing'}]\nconfig['actions'] = [\n {'variables': {'new_slot': \"{{ states('sensor.sparkx_i7_active_filament_slot') }}\", 'old_slot': \"{{ states('input_text.sparkx_i7_print_spool_latch') }}\", 'cur': \"{{ states('sensor.sparkx_i7_used_material_length') | float(0) }}\"}},\n {'choose': [\n {'conditions': {'condition': 'trigger', 'id': 'print_started'},\n 'sequence': [\n {'action': 'input_number.set_value', 'target': {'entity_id': 'input_number.sparkx_i7_spoolman_length_checkpoint'}, 'data': {'value': '{{ cur }}'}},\n {'action': 'input_number.set_value', 'target': {'entity_id': ['input_number.sparkx_i7_slot_1_grams', 'input_number.sparkx_i7_slot_2_grams', 'input_number.sparkx_i7_slot_3_grams', 'input_number.sparkx_i7_slot_4_grams']}, 'data': {'value': 0}},\n {'action': 'input_text.set_value', 'target': {'entity_id': 'input_text.sparkx_i7_print_spool_latch'}, 'data': {'value': \"{{ new_slot if new_slot not in ['unknown', 'unavailable'] else '' }}\"}}\n ]},\n {'conditions': {'condition': 'template', 'value_template': \"{{ old_slot not in ['', 'unknown', 'unavailable'] and new_slot not in ['', 'unknown', 'unavailable'] and new_slot != old_slot }}\"},\n 'sequence': [\n {'variables': {'grams': \"{{ (([cur | float - (states('input_number.sparkx_i7_spoolman_length_checkpoint') | float(0)), 0] | max) * (states('input_number.sparkx_i7_filament_g_cm') | float(0.03))) | round(2) }}\", 'color': \"{{ states('sensor.sparkx_i7_cfs_' ~ (old_slot | lower | replace(' ', '_')) ~ '_color') | replace('#', '') | lower }}\"}},\n {'variables': {'spool_id': \"{% set ns = namespace(id='') %}{% for e in integration_entities('spoolman') %}{% if e.endswith('_color_hex') and (states(e) | replace('#', '') | lower) == color %}{% set b = e[:-10] %}{% if (states(b) | float(0)) > 0 and ns.id == '' %}{% set ns.id = states(b ~ '_id') %}{% endif %}{% endif %}{% endfor %}{{ ns.id }}\"}},\n {'if': {'condition': 'template', 'value_template': '{{ grams | float > 0 }}'},\n 'then': [{'choose': [\n {'conditions': {'condition': 'template', 'value_template': \"{{ spool_id not in ['', 'unknown', 'unavailable'] }}\"},\n 'sequence': [\n {'action': 'spoolman.use_spool_filament', 'data': {'id': '{{ spool_id | int(0) }}', 'use_weight': '{{ grams | float(0) }}'}},\n {'if': {'condition': 'template', 'value_template': \"{{ old_slot.split(' ')[-1] in ['1', '2', '3', '4'] }}\"},\n 'then': [{'action': 'input_number.set_value', 'target': {'entity_id': \"input_number.sparkx_i7_slot_{{ old_slot.split(' ')[-1] }}_grams\"}, 'data': {'value': \"{{ (states('input_number.sparkx_i7_slot_' ~ old_slot.split(' ')[-1] ~ '_grams') | float(0)) + (grams | float) }}\"}}]}\n ]}\n ], 'default': nt}]},\n {'action': 'input_number.set_value', 'target': {'entity_id': 'input_number.sparkx_i7_spoolman_length_checkpoint'}, 'data': {'value': '{{ cur }}'}},\n {'action': 'input_text.set_value', 'target': {'entity_id': 'input_text.sparkx_i7_print_spool_latch'}, 'data': {'value': '{{ new_slot }}'}}\n ]},\n {'conditions': {'condition': 'template', 'value_template': \"{{ old_slot in ['', 'unknown', 'unavailable'] and new_slot not in ['', 'unknown', 'unavailable'] }}\"},\n 'sequence': [\n {'action': 'input_number.set_value', 'target': {'entity_id': 'input_number.sparkx_i7_spoolman_length_checkpoint'}, 'data': {'value': '{{ cur }}'}},\n {'action': 'input_text.set_value', 'target': {'entity_id': 'input_text.sparkx_i7_print_spool_latch'}, 'data': {'value': '{{ new_slot }}'}}\n ]}\n ]}\n]\nconfig['mode'] = 'queued'\nconfig['max'] = 10", "config_hash": "ce44e8bfd2aea2cf", "take_control_of_blueprint": false, "category": null, "wait": true, "enabled": null, "run_actions": false, "MandatoryBPS": true, "BestPracticeKey": null}, "execution_time_ms": 2299.7162342071533, "success": true, "error_message": null, "response_size_bytes": 85218, "user_context": null} diff --git a/automations.yaml b/automations.yaml index c27e4fa8..7d63eaba 100644 --- a/automations.yaml +++ b/automations.yaml @@ -4426,6 +4426,12 @@ mode: single - id: '1791477238762' alias: SparkX i7 - Latch print spool for Spoolman + description: 'Segment tracking for Spoolman: at print start resets the length checkpoint + and per-slot gram counters and latches the active CFS slot. On every slot change + mid-print, deducts the exact extrusion of the finished segment (used_material_length + delta x g/cm) from the Spoolman spool matching the previous slot color hex (only + if that spool has remaining weight), accumulates grams per slot, notifies on no-match, + then re-baselines the checkpoint and advances the latch.' triggers: - trigger: state entity_id: sensor.sparkx_i7_print_status @@ -4433,40 +4439,131 @@ id: print_started - trigger: state entity_id: sensor.sparkx_i7_active_filament_slot - - trigger: state - entity_id: - - sensor.sparkx_i7_cfs_box_1_slot_1_color - - sensor.sparkx_i7_cfs_box_1_slot_2_color - - sensor.sparkx_i7_cfs_box_1_slot_3_color - - sensor.sparkx_i7_cfs_box_1_slot_4_color + id: slot_changed conditions: - condition: state entity_id: sensor.sparkx_i7_print_status state: printing actions: + - variables: + new_slot: '{{ states(''sensor.sparkx_i7_active_filament_slot'') }}' + old_slot: '{{ states(''input_text.sparkx_i7_print_spool_latch'') }}' + cur: '{{ states(''sensor.sparkx_i7_used_material_length'') | float(0) }}' - choose: - conditions: condition: trigger id: print_started sequence: + - action: input_number.set_value + target: + entity_id: input_number.sparkx_i7_spoolman_length_checkpoint + data: + value: '{{ cur }}' + - action: input_number.set_value + target: + entity_id: + - input_number.sparkx_i7_slot_1_grams + - input_number.sparkx_i7_slot_2_grams + - input_number.sparkx_i7_slot_3_grams + - input_number.sparkx_i7_slot_4_grams + data: + value: 0 - action: input_text.set_value target: entity_id: input_text.sparkx_i7_print_spool_latch data: - value: '' - - variables: - spool_id: "{%- set slot = states('sensor.sparkx_i7_active_filament_slot') -%}\n{%- - set ce = 'sensor.sparkx_i7_cfs_' ~ (slot | lower | replace(' ', '_')) ~ '_color' - -%}\n{%- set color = states(ce) | replace('#', '') | lower -%}\n{%- set ns - = namespace(id = '') -%}\n{%- for e in integration_entities('spoolman') -%}\n - \ {%- if e.endswith('_color_hex') and (states(e) | lower) == color -%}\n {%- - set ns.id = states(e[:-10] ~ '_id') -%}\n {%- endif -%}\n{%- endfor -%}\n{{ - ns.id }}" - - condition: template - value_template: '{{ spool_id not in ['''', ''unknown'', ''unavailable''] }}' - - action: input_text.set_value - target: - entity_id: input_text.sparkx_i7_print_spool_latch - data: - value: '{{ spool_id }}' - mode: single + value: '{{ new_slot if new_slot not in [''unknown'', ''unavailable''] else + '''' }}' + - conditions: + condition: template + value_template: '{{ old_slot not in ['''', ''unknown'', ''unavailable''] and + new_slot not in ['''', ''unknown'', ''unavailable''] and new_slot != old_slot + }}' + sequence: + - variables: + grams: '{{ (([cur | float - (states(''input_number.sparkx_i7_spoolman_length_checkpoint'') + | float(0)), 0] | max) * (states(''input_number.sparkx_i7_filament_g_cm'') + | float(0.03))) | round(2) }}' + color: '{{ states(''sensor.sparkx_i7_cfs_'' ~ (old_slot | lower | replace('' + '', ''_'')) ~ ''_color'') | replace(''#'', '''') | lower }}' + - variables: + spool_id: '{% set ns = namespace(id='''') %}{% for e in integration_entities(''spoolman'') + %}{% if e.endswith(''_color_hex'') and (states(e) | replace(''#'', '''') + | lower) == color %}{% set b = e[:-10] %}{% if (states(b) | float(0)) + > 0 and ns.id == '''' %}{% set ns.id = states(b ~ ''_id'') %}{% endif + %}{% endif %}{% endfor %}{{ ns.id }}' + - if: + condition: template + value_template: '{{ grams | float > 0 }}' + then: + - choose: + - conditions: + condition: template + value_template: '{{ spool_id not in ['''', ''unknown'', ''unavailable''] + }}' + sequence: + - action: spoolman.use_spool_filament + data: + id: '{{ spool_id | int(0) }}' + use_weight: '{{ grams | float(0) }}' + - if: + condition: template + value_template: '{{ old_slot.split('' '')[-1] in [''1'', ''2'', ''3'', + ''4''] }}' + then: + - action: input_number.set_value + target: + entity_id: input_number.sparkx_i7_slot_{{ old_slot.split(' ')[-1] + }}_grams + data: + value: '{{ (states(''input_number.sparkx_i7_slot_'' ~ old_slot.split('' + '')[-1] ~ ''_grams'') | float(0)) + (grams | float) }}' + default: + - action: notify.persistent_notification + data: + title: 'Spoolman: no spool for slot color' + message: 'Segment of {{ grams }} g from {{ old_slot }} (color #{{ color + | upper }}) could not be deducted - no Spoolman spool with remaining + weight matches that color. Register the new roll in Spoolman, then + adjust manually if needed.' + - action: notify.mobile_app_francos_ipad + data: + title: 'Spoolman: no spool for slot color' + message: 'Segment of {{ grams }} g from {{ old_slot }} (color #{{ color + | upper }}) could not be deducted - no Spoolman spool with remaining + weight matches that color. Register the new roll in Spoolman, then + adjust manually if needed.' + - action: notify.mobile_app_sm_f956w + data: + title: 'Spoolman: no spool for slot color' + message: 'Segment of {{ grams }} g from {{ old_slot }} (color #{{ color + | upper }}) could not be deducted - no Spoolman spool with remaining + weight matches that color. Register the new roll in Spoolman, then + adjust manually if needed.' + - action: input_number.set_value + target: + entity_id: input_number.sparkx_i7_spoolman_length_checkpoint + data: + value: '{{ cur }}' + - action: input_text.set_value + target: + entity_id: input_text.sparkx_i7_print_spool_latch + data: + value: '{{ new_slot }}' + - conditions: + condition: template + value_template: '{{ old_slot in ['''', ''unknown'', ''unavailable''] and new_slot + not in ['''', ''unknown'', ''unavailable''] }}' + sequence: + - action: input_number.set_value + target: + entity_id: input_number.sparkx_i7_spoolman_length_checkpoint + data: + value: '{{ cur }}' + - action: input_text.set_value + target: + entity_id: input_text.sparkx_i7_print_spool_latch + data: + value: '{{ new_slot }}' + mode: queued + max: 10