At times I want to do a dictionary lookup where the keys (strings, of course) are all digits.
For example, I have a input_text with a (string) value of "12345678":
{% set sensor_value = states('input_text.test_integer' ) %}
sensor_value={{ sensor_value }}
is_string={{ sensor_value is string }}
{% set codes = {"12345678":"Found the key"} %}
{{ codes.get(sensor_value,'Nope, did not find it') }}
Resulting in what I would expect -- the text_sensor is a string, and therefore I can look it up in the dictionary:
sensor_value=12345678
is_string=True
Found the key
Now, do that in an automation:
triggers: []
conditions: []
actions:
- variables:
codes:
"12345678": Found the key
sensor_value: "{{ states('input_text.test_integer' ) }}"
is_string: "{{ sensor_value is string }}"
lookup: "{{ codes.get( sensor_value, 'No, did not find the key') }}"
force_string: >-
{{ codes.get(sensor_value|string, 'No, did not find the key by string')
}}
And then run and look at the trace:
context:
id: 01KVTE4GFBECCQFQQ06GYVQYFK
parent_id: 01KVTE4GFBCCNX9JY75RCT5GHP
user_id: null
codes:
'12345678': Found the key
sensor_value: 12345678
is_string: false <---- Now it's not a string
lookup: No, did not find the key
force_string: Found the key
Are the templates pre-processed before parsing the JSON and the Python JSON parser implicitly turns it into an integer/float? Or is there some other code that is turning it into an integer?
It is a side effect of how templates are handled. Templates themselves are always strings, which would make them incapable of returning any other type. That is why when they represent a numerical value or compound object, they are converted to that (but not datetime by the way).
Besides forcing as you did, an often used workaround is to return a compound object, because inside that types re kept as they are. So:
should keep key a string no matter what. And to get it:
original_string: >-
{{ codes.get(sensor_value.key, "You should not see this default")
}}
You could also return an array, it would also keep the type of the elements inside.
When using only the template inside the template tester of the developer tools, you get to see the type of the data the template will convert to. If you put other stuff around it, the output will of course always be string because it is no longer a plain type.
Ok, there's some code somewhere that renders the template and then the value returned matches a regex for an integer and then converts to integer (or float, I assume).
And my dev tools version only does:
{% set sensor_value = states('input_text.test_integer' ) %}
Which is setting the value directly, not within a {{ }} template.
It's a post process when assigning the template result to a variable. I.e. it happens here:
So when the next variable uses it, this fails:
If you want to force a string, return complex types.
- variables:
codes:
"12345678": Found the key
sensor: "{{ dict(state=states('input_text.test_integer')) }}"
is_string: "{{ sensor.state is string }}"
lookup: "{{ codes.get(sensor.state, 'No, did not find the key') }}"
At some point, HA swapped to annotatedyaml library, which tracks where yaml exists in files. This is a byproduct of the way that library is used in HA.