I have configured a helper of the type template to monitor, what type of filament my Bambu Lab P1S is currently printing.
The template goes like this:
{% set global = namespace(active_filament="N/A") %}
{% for spool in states.sensor -%}
{%- if (spool.name).split(' ')[0] == 'P1S_123456789ABCDEF_AMS_1' and (spool.name).split(' ')[1] == 'Slot' %}
{%- if (state_attr((spool.entity_id), "active")) %}
{% set global.active_filament = state_attr((spool.entity_id), "type") %}
{% endif -%}
{% endif -%}
{% endfor %}
{{ global.active_filament }}
The template/helper works as expected, but the downside is that the for loop runs for nearly 650 sensors! And only within the loop do the if-statements filter the four relevant sensors. So the names of all 650 sensors are split and matched. That doesn’t feel lean.
So I wondering if there is a more efficient way to code the template. The relevant sensors have these attributes:
How much more reliability?
You need to search for a pattern in your loop, which seems to be a big part of the name, so if the name change then the search will likely fail too.
Thanks a lot for the code. It works.
To make it more reliable, I made the regular expression more common. Also I want one string instead of a list (there always can be 0 or 1 active filaments, not more) and if no filament is active, the string is set to “N/A”.
The question still stands. How much more reliability do you get.
Petros script actually removes a bit of the flexibility too, because your script searched for “P1S_123456789ABCDEF_AMS_1” and “Slot”, while his only search for “P1S_123456789ABCDEF_AMS_1 Slot”.
That’s not how the code works. The regex does not remove any flexibility. When you take a string and split it based on spaces, then check the first and second item. it’s identical to a regex that has the space checking both.
i.e.
match('^P1S_123456789ABCDEF_AMS_1 Slot', value)
is functionally identical to
a, b = value.split(' ')
a == 'P1S_123456789ABCDEF_AMS_1' and b == 'Slot'
Anyways, the regex he’s using is now more flexible than what I wrote initially anyways.
^P1S_(.)+_AMS_\d Slot \d
Looks for strings starting with P1S_ that contain any number of characters and numbers until _AMS_. It then checks for a valid number, then a space, then the word Slot then a space, lastly a valid number.
All in all, his method is robust and requires little maintenance. It’s a route I would take.
Petros already made clear, how his regex works.
As mentioned in my last post, I customized the regex to be more common. That’s what the template in my helper actually looks like:
{{ states.sensor
| selectattr('name', 'match', '^P1S_(.)+_AMS_\d Slot \d')
| selectattr('attributes.active', 'defined')
| selectattr('attributes.active', 'eq', True)
| map(attribute='attributes.type')
| list
| first
| default or "N/A" }}
You could also take it a bit further. Maybe some time I get a X1C
Okay, I am not really on good terms with regex, but the first few iterations did not really look like regex.
The ones with ^P1S_(.)+_AMS_\d Slot \d makes sense to me flexibility-wise.
The first post did not have that.
Your reply had it, but I can not see how a regex expression of ^P1S_123456789ABCDEF_AMS_1 Slot is different from a substring match with P1S_123456789ABCDEF_AMS_1 Slot.
It might be my lack of regex knowledge, but that clouds it here though.
To me it was a lot of jumping around that was not needed.
First take a line, then split it up and then compare each element of the split with a fixed value.
Instead of just combining the split and compare with that.
And because the elements were pretty fixed, then it made hardly no sense to search for them, because the compare pattern would be almost the complete string to compare against.
^(P1S|X1C)_(.)+_AMS_\d Slot \d makes sense to me and if you used splits then there would probably be elements skipped, which makes it flexible.
I did not try to optimize the original code, but questioned its premise.