JSON state string with for loop shows one character per line when used in a template

Hi All,

I’ve created a sensor that puts a JSON string inside the state of the sensor. I’m trying to create a template to display these values in a card. The content of the state attribute is like:

[{
	"TargetDepartureTime": "10:56",
	"Delay": "0"
}, {
	"TargetDepartureTime": "11:06",
	"Delay": "0"
}, {
	"TargetDepartureTime": "11:46",
	"Delay": "0"
}, {
	"TargetDepartureTime": "11:56",
	"Delay": "0"
}, {
	"TargetDepartureTime": "12:06",
	"Delay": "0"
}]

I’ve tested the JSON with JSONLint and it seems valid JSON.

When I open the template designer and enter the content below, the content is parsed correctly:

{% set ov_json = [{
	"TargetDepartureTime": "10:56",
	"Delay": "0"
}, {
	"TargetDepartureTime": "11:06",
	"Delay": "0"
}, {
	"TargetDepartureTime": "11:46",
	"Delay": "0"
}, {
	"TargetDepartureTime": "11:56",
	"Delay": "0"
}, {
	"TargetDepartureTime": "12:06",
	"Delay": "0"
}] %}

{% for value in ov_json %}
{% if value.Delay | int > 0 %}
{% set message = '- Delay: ' + value.Delay %}
{% endif %}
Departure: {{ value.TargetDepartureTime }}{{message}}
{% endfor %}

But when I change the ov_json variable to states.sensor.tram_6__stop_9505.state (this is the custom sensor) I get the output below:

Template:

{% set ov_json = states.sensor.tram_6__stop_9505.state | tojson %}

{% for item in ov_json %}
For item: {{ item }}
{% endfor %}

Result:

For item: "
For item: [
For item: {
For item: \
For item: "
For item: T
For item: a
For item: r
For item: g
For item: e
For item: t
For item: D
For item: e
For item: p
For item: a
For item: r, etc....

I’ve also got a hint to use | tojson but this makes no difference…

The question is, did I do something wrong on coding the sensor, or am I missing something important here…

I do have to admit I’m a noob on templating, just started experimenting.

Put your json object into an attribute, set the state equal to the first object in the list. When accessing the attribute, it should return a list of dictionaries, not a string. State always returns a string.

I’d even potentially go as far as setting the state to the first time, not the first dictionary. Then add a delay attribute as well.

Could name the attribute with the json “departures”.

Hi @petro, thanks for you reply. So If I understand it correctly:

  • Add Attribute (property) departure (first arrival)
  • Add Attribute (property) delay (first arrival delay)
  • Add Attribute (property) departures (list of dicts)
  • Change state to sting (departure + delay)
  • Add STATE_UNKNOWN to sensor when there is no data yet… (a small reminder for myself :slight_smile: )

After this I think I should be able to access the Dict trough states.sensor.tram_6__stop_9505.departures

Then the for loop will work right?

yes, but small change: states.sensor.tram_6__stop_9505.attributes.departures or state_attr('sensor.tram_6__stop_9505','departures')

Nice, this worked. I now have to find something that will display the values on new lines. All departure times are now on the same line.

22:29 - Delay: 122:4523:0023:3023:45

Hi,

Even with the changes to the sensor that returns a dict to the departures property and below template every value is placed next to eachother… I’m confused

          {%- if state_attr('sensor.tram_6__stop_9505','departures')[0] is defined -%}
            {%- if state_attr('sensor.tram_6__stop_9505','departures')[0]['Delay'] | int > 0 -%}
              {%- set message = ' - Vertraging: ' + state_attr('sensor.tram_6__stop_9505','departures')[0]['Delay'] -%}
            {%- endif -%}
            {{ state_attr('sensor.tram_6__stop_9505','departures')[0]['TargetDepartureTime'] }}{{ message }}
          {%- endif -%}
          {%- if state_attr('sensor.tram_6__stop_9505','departures')[1] is defined -%}
            {%- if state_attr('sensor.tram_6__stop_9505','departures')[1]['Delay'] | int > 0 -%}
              {%- set message = ' - Vertraging: ' + state_attr('sensor.tram_6__stop_9505','departures')[1]['Delay'] -%}
            {%- endif -%}
            {{ state_attr('sensor.tram_6__stop_9505','departures')[1]['TargetDepartureTime'] }}{{ message }}
          {%- endif -%}

Am I missing something?

Yes, they need to be separate sensors:

sensor:
  - platform: template
    sensors:
      departure1:
        friendly_name: Departure 1
        value_template: > 
          {%- set i = 0 %}
          {%- set departure = state_attr('sensor.tram_6__stop_9505','departures')[i] if state_attr('sensor.tram_6__stop_9505','departures')[i] is defined else None %}
          {%- if departure %}
            {%- set message = ' - Vertraging: %s'%departure['Delay'] if departure['Delay'] | int > 0 else '' %}
            {{ departure['TargetDepartureTime'] }}{{ message }}
          {%- else %}
            No Departure
          {%- endif %}
      departure2:
        friendly_name: Departure 2
        value_template: > 
          {%- set i = 1 %}
          {%- set departure = state_attr('sensor.tram_6__stop_9505','departures')[i] if state_attr('sensor.tram_6__stop_9505','departures')[i] is defined else None %}
          {%- if departure %}
            {%- set message = ' - Vertraging: %s'%departure['Delay'] if departure['Delay'] | int > 0 else '' %}
            {{ departure['TargetDepartureTime'] }}{{ message }}
          {%- else %}
            No Departure
          {%- endif %}

EDIT: Multline sensors are not possible. They have to be 100% separate if you don’t want them sharing the same line in the UI.

1 Like

Oh nice! Thanks for the complete example. This worked like a charm.

Looks nice now! And with your hints the sensor evolved nicely - Screenshot

Thanks!! :slight_smile:

1 Like