Exctract information from JSON

Hi again!

I have been working more on my project.
With the solution that Troon gave me:

sensor:
  - platform: rest
    resource: YOUR_URL
    name: Train times list
    value_template: "{{ value_json['Departure']|selectattr('directionFlag','eq','2')|map(attribute='time')|list }}"
    scan_interval: 120

I was able to create a sensor that gave me a list, but the next part didnt work.
Also when I worked in the template editor, it intepreted the result from

{{ states('sensor.train_times_list') }} as a string I think, making it again impossible to extraxt every time as its on element. I think when a sensor gives its state it does so only as a string? I am not sure.

After a little bit of reading I was able to then take this string and turn it into a list again with the code below, so it is possible to extract every element as its own time. Also removing the [, ] and ’ from the results.

{% set traintimes = states('sensor.train_times_list').split(',') %}
{% set traintimes = ' '.join(traintimes).replace('[','').replace(']','')
.replace("'",'').split() %}
{{ traintimes[0] }}

This produces just for example 14:31:00
Which is in the nice format I want.

But now I dont know how to get it into a sensor?
My sensors.yaml file accepts many things, but the config check alway rejects it.

Currently the sensors.yaml file looks only like this:


# Extracting list of train times into sensor

  - platform: rest
    resource: https://api.resrobot.se/v2.1/departureBoard?id=740000775&duration=60&maxJourneys=-1&products=16&passlist=0&lang=sv&format=json&requestId=random%20string&accessId=db5f04e0-7fa1-403d-8a2c-bba3641e105b
    name: Train times list
    value_template: "{{ value_json['Departure']|selectattr('directionFlag','eq','2')|map(attribute='time')|list }}"
    scan_interval: 120
    

    

        

All sensor states are strings. What bit of my solution above in post #9 didn’t work? Please post what the output of this in the template editor (Developer Tools / Template) gives:

{{ states('sensor.train_times_list') }}
{% if states('sensor.train_times_list')|from_json|length > 0 %}
  {{ (states('sensor.train_times_list')|from_json)[0] }}
{% else %}
  unavailable
{% endif %}

The from_json filter turns a string that looks like JSON into an actual object.

It gives me this in the template editor:

JSONDecodeError: unexpected character: line 1 column 2 (char 1)

OK: what about just the first line on its own?

{{ states(‘sensor.train_times_list’) }}

gives this:

[
  "15:25:00",
  "15:29:00",
  "15:36:00",
  "15:44:00",
  "15:51:00",
  "15:55:00",
  "15:59:00",
  "16:06:00",
  "16:14:00",
  "16:21:00",
  "16:25:00"
]

Also does say on top of the result box:
Result type: list

Even though I do not get it to behave as a list

This is also the part that did not work from post 9

I managed to create a sensor now with this in my sensors.yaml

  - platform: template
    sensors:
      train1:
        friendly_name: Next train
        value_template: >
          {% set traintimes = states('sensor.train_times_list').split(',') %}
          {% set traintimes = ' '.join(traintimes).replace('[','').replace(']','').replace("'",'').split() %}
          {{ traintimes[0] }}

That created a sensor that only spits out HH:MM:SS
Which is what I am going for.
I think now the only step left, if it is possible is to convert this into minutes remaning.

Psuedo code:

For each time in {{ traintimes }} convert HH:MM:SS into minutes remaning.
Sensor:
Next train:
value_template:
if traintimes[0] < 0 minutes print traintimes[0]
else print traintimes[1]
Later train 1:
if Next train == traintimes[0] then print traintimes[1]
else print traintimes[2]

And so on.
This would always ensure that the next train is displayed in the sensor Next train.
Because I cannot spam the API url all the time, then ill be locked out.
And then the value of the next train can become negative when the time for when it was supposed to depart has passed because the API resource is not updated.

If any of this makes any sense.

Instead of

{{ traintimes[0] }}

use

{{ traintimes|select('>', now().isoformat()[11:19])|first }}

Or if you want to pull a few times:

{{ (traintimes|select('>', now().isoformat()[11:19])|list)[0] }}
# for the next train
{{ (traintimes|select('>', now().isoformat()[11:19])|list)[1] }}
# for the second train
{{ (traintimes|select('>', now().isoformat()[11:19])|list)[2] }}
# for the third train

I dont quite understand what this does?

{{ traintimes[0] }} listens for the sensor that you created earlier:

The train_times_list, which updates every 2 minutes right now, because of the API limitations.
Is the goal with this code

Just that itself updates every minute?

EDIT: I am sorry, I got it now, by just looking at the code properly.
So if I interpret it correctly it will only select times that are greater than right now. And update every minute. Right?

Yes, that’s correct. The isoformat()[11:19] generates a “now” time string that matches the list from the other sensor.

I finally got all of the details I wanted!

The last key was this: How to calculate / subtract two date.time from eachother? - #23 by mcfrojd

So know my sensos.yaml looks like this:

  - platform: rest
    resource: API Url
    name: Train times list
    value_template: "{{ value_json['Departure']|selectattr('directionFlag','eq','2')|map(attribute='time')|list }}"
    scan_interval: 120
    

    

  - platform: template
    sensors:
      train1:
        friendly_name: Next train
        value_template: >
          {% set traintimes = states('sensor.train_times_list').split(',') %}
          {% set traintimes = ' '.join(traintimes).replace('[','').replace(']','').replace("'",'').split() %}
          {% set time = (traintimes|select('>', now().isoformat()[11:19])|list)[0] %}
          {{ (((as_timestamp(strptime(time, "%H:%M:%S"))) - as_timestamp( strptime( now().strftime("%H:%M"), "%H:%M") )) /60) | int}}

      train2:
        friendly_name: Later train
        value_template: >
          {% set traintimes = states('sensor.train_times_list').split(',') %}
          {% set traintimes = ' '.join(traintimes).replace('[','').replace(']','').replace("'",'').split() %}
          {{ (traintimes|select('>', now().isoformat()[11:19])|list)[1] }}

No it always shows how many minutes are left until the next train depart only in the direction that I want, and show the next train after that as well. And with your genius code, Troon, it always updates correctly. So if a train has passed it will no longer show that train as “Next train”

No I can modify this list as I want and use it in sensors and notifications and so on.
I learned so much from this! So thank you so much for helping me!

1 Like