I’ve been beating this around for half a day, and decided to get some expert help.
I want to test a list to make sure it’s a valid RGB color list.
So first I need to make sure it’s a list, and that the list has 3 members. Then I need to test that each member is a number, an integer, and between 0 & 255.
If all that is true return true. If any is not true, return false.
I have pieces parts working, but am ending up with multiple true false things.
{%- set _rgbl = [125,11.3,183] %}
{%- if _rgbl is list and _rgbl | count == 3 %}
{%- for clr in _rgbl %}
{%- if is_number(clr) and int(clr,0) == clr and 0 <= clr <= 255%}
True
{%- else %}
False
{%- break %}
{%- endif %}
{%- endfor %}
True
{%- else %}
False
{%- endif %}
That example is set to fail on the second number.
So the question is really how to get the first test result at the list level melded with the test of the list member test results so that the return is true or false.
What about moving the if inline with the for, then using loop variables:
{%- if _rgbl is list and _rgbl | count == 3 %}
{%- for clr in _rgbl if is_number(clr) and int(clr,0) == clr and 0 <= clr <= 255 %}
{% if loop.last %}
{{ loop.index == 3 }}
{%- endif %}{% endfor%}{%- endif %}
But, you need to remove the count check in the first if. Otherwise, when not three, you just drop out with nothing.
Oh, don’t remove it. Just put a false before the last endif. Because, if not a list, you just drop out too.
{%- if _rgbl is list and _rgbl | count == 3 %}
{%- for clr in _rgbl if is_number(clr) and int(clr,0) == clr and 0 <= clr <= 255 %}
{% if loop.last %}
{{ loop.index == 3 }}2
{%- endif %}{% endfor%}{% else %}False{%- endif %}
{{ _rgbl is list and
_rgbl | select('integer') | list | count == 3 and
_rgbl | select('<', 0) | list | count == 0 and
_rgbl | select('>', 255) | list | count == 0 }}
Confirm it's a list and
All three items are integers and
None are less than 0 and
None are greater than 255
Both solutions work. I think I’m going with the first one because that’s where my head was trying to get to, but @123 you solution is certainly cleaner.
I’m working on my Color Multi tool macro, and will put your names @Didgeridrew@jeffcrum in the credits as contributors. Thanks for the help!
So this is weird. Only one element comparison is required to fail the test if all numbers are positive yet two elements are required to fail the test for negative numbers:
I have to write one for xy and hs color as well, I’ll use his for xy and credit @123 .
That way I’ll be able to find both ways to do it when I need to again.
If you prefer a for-loop, just throw all the tests into its if section and count how many list items pass all of them. If the count is 3 then it’s true otherwise false.
{%- set ns = namespace(x = 0) -%}
{%- for clr in _rgbl if _rgbl is list and _rgbl | count == 3 and
clr is integer and 0 <= clr <= 255 %}
{%- set ns.x = ns.x + 1 -%}
{%- endfor %}
{{ ns.x == 3 }}
You can use this to remove any even number of negative signs (or reduce an odd number of negative signs to just one).
{% set _rgbl = tuple(_rgbl) | list %}
However I believe the double-negative value passed the original battery of tests because a double-negative is understood to be a positive value … but I don’t know if Home Assistant will accept it as a valid value for setting a light’s RGB value. Worth testing to see if the conversion I suggested above is even needed.
I’m going to ignore the double negative… If they have that in there they deserve to get an error.
For completeness, here are the xy and hs list checks I’m using.
{{- _xyl is list and
_xyl | select('number') | list | count == 2 and
_xyl | select('<', 0) | list | count == 0 and
_xyl | select('>', 1) | list | count == 0 }}
{{- _hsl is list and
_hsl | select('number') | list | count == 2 and
_hsl | select('<', 0) | list | count == 0 and
_hsl[0] <= 100 and
_hsl[1] <= 360 }}