I have two sensors; both store data in attributes as JSON arrays (or a list of dicts, if you wish). In both cases, one of the attributes carries a string that represents ISO formatted timestamp (see below)
How surprising is it that printing them in the template editor results in a different format? One of them remains a text while another is automatically converted into a datetime object.
How can it be explained?
How is it gonna behave within trigger templates/automations?
Fortunately, both forms are successfully accepted by the as_datetime() method
Templates only return strings. After a template is produces a string result, a post process is ran that attempts to resolve the object type. This process is not recursive, it only types the top level object. So for dictionaries or lists, interior objects are not converted to something HA can pass around freely. This is done for 2 reasons:
recursion can cause problems and it’s avoided at all costs.
There are times where you need to bypass the resolver to ensure you keep specific object types. The only way around that is to nest values inside a dictionary or list.
What you’re seeing in your top dictionary, is a string representation of a datetime object.
What you’re seeing in the bottom dictionary, is the actual object unaltered by the resolver.
Thanks for prompt reaction.
I would understand it if each of those attributes were nested differently within the structure. Both are on the same level:
attributes.data.time
vs
attributes.detailedForecast.period_start
it depends on what’s creating that data. Core does not allow this type of data in attributes, so this is likely caused by whatever integration is producing those values.
This gets even more interesting…
I never expected that attributes carry information about complex datatypes.
The pv data comes from the Solcast integration. And as shown above appears as datetime objects.
The prices are from the template sensor. Although it converts time to the object, it put it into a JSON structure. Maybe it’s the reason?
- name: "Today Spot Electricity Prices"
state: "{{ now() }}"
attributes:
period: "15 minutes"
data: >
{% set data = namespace(prices=[]) %}
{% for key, val in states.sensor.current_spot_electricity_price_15min.attributes.items() %}
{% if key | as_datetime(0) != 0 %}
{% set obj = { "time" : key, "price" : val | round(7) } %}
{% set data.prices = data.prices + [obj] %}
{% endif %}
{% endfor %}
{{ data.prices }}
My knowledge of yaml/jinja/templating is still vely limited. For a while, I was thinking that the reason lies in using JSON as a struct for data. But after rewriting to the dict, the behavior did not change. The datetype of time attribute remains text
- name: "Today Spot Electricity Prices Dict Test"
state: "{{ now() }}"
attributes:
period: "15 minutes"
data: >
{% set data = namespace(prices=[]) %}
{% for key, value in states.sensor.current_spot_electricity_price_15min.attributes.items() %}
{% if key | as_datetime(default=None) %}
{% set data.prices = data.prices + [dict(time=key, price=value | round(7))] %}
{% endif %}
{% endfor %}
{{ data.prices }}
Is it possible to write a template sensor that creates the same structure as above but with datetime objects?
Your answer pointed me to where I made a mistake.
I was confident that I had converted the string to a datetime object using as_datetime, but I only did that for condition of the if statement—while still assigning the original string value.
Once I properly convert the value during assignment, it stores a datetime object.
Thanks for the assistance.
Edit: almost… It fixes the value, but such attribute value stored this way is no more json array. It’s the text instead. unless there is workaround, it seems that integrations can create attributes that carry info about datatype, while it’s not possible with template sensors (???)
Yes, as I explained above, the resolver for templates does not recursively type things. Meaning objects remain as-is when passed through the resolver, which cannot be serialized. Sorry if that wasn’t clear.