Exctract information from JSON

OK, but I was thinking about multiple train-lines, not just the time
EDIT: the OP should chime in and present their view

You can create separate lists per line; and if needs be, constrain the number of items with something like (foo|list)[:10].

Clear but if stored in state, the limit is (?) 255. If the OP is ok then this is the best (!) option indeed as putting stuff in attribs and then adding new sensor just makes is ‘less nice’

You don’t need to stuff this in the attributes, you can use the rest integration (instead of using the rest platform)

quite sometimes it is not needed to create multiple sensors with rest. I use a workaround to collect data into a single sensor with substantial attrib…this allows me to run a graph on it which I would not be able to do if it was split into sensors (in the line of weather ‘forecast’)

Hi Troon,

I apologize for this. I thought that my original post had drowned in other requests and forgotten about.
Since I am a beginner I thought instead of asking again for help (which I eventually ended up doing) I would try ChatGPT which I had never tried before and only heard and read about in the news.
The code GPT generated looked cool to me, so I thought it might work, and thought perhaps there only was some slight error that someone could point out.
I was wrong.

Thank you so much for the work you put in to create this for me.
I am not indifferent to the time people have to put in to help others, so I am very grateful.
I will repay you by learning a lot more myself so that I dont have to waste any more time of others, and perhaps be able to help someone else down the road.

Perhaps my biggest problem when learning this (I have only been at Home Assistant and Raspberry Pi and coding for abouth a month) is that sometimes I dont even know where to begin looking for information.

Tutorials on YouTube only tend to tell you what to do, not why you do it, or how it works.
And for this case there was no tutorial for my exact request.

If you could point me in a direction where to start reading to create sensors, templates or integrate stuff from the web, I assure you I will read. I am not trying to just get other people to do my work, I want to learn.

As for you last comment,
HA said I needed code and that would fix the code being displayed in my post.
Thats why I probably havent done it correctly.
Ill try and figure out how to do what you told me.

Thank you again.

1 Like

Yes. With the extraction of trains only going one direction I think it will only be about maximum of 10-11 trains. 11 characters per time x 10 will be 110. So it should work then. Thank you for helping me!

Thank you so much for helping me! I will post back if I make it work, or not.
I think I have stupidly given the impression that perhaps I am lazy and only want you to fix my problems.
I want to say that I am a very beginner, and because I think this is so much fun that I have spent quite a lot of time to get to where I am now.
I was super exited when I could just actually get a JSON file from an API! Hehe
I didnt even know what that was a month ago.

I would be very grateful if someone could point me in a direction where I can read more about how to use custom code in Home Assistant. I have done some easy Python code, which I understand HA is based on, but I have to use Jinja2 when coding a template?
And I still have to learn how to use the configuration.yaml file correctly and how to reference to other files and how to create custom stuff.
When I dont know where to start it feels like it would take forever to educate oneself. But its fun!

Thank you again for your time!

Absolutely no need to apologise. Hope it works out for you, and do come back with code and specific questions if not. Just amused me that we’ve got to the point where it’s almost reasonable to ask a general purpose machine to write code…

HA has excellent documentation — I’ve linked to some of it already but the starting point is here:

It’s better to work through this than use YouTube tutorials — HA has evolved a lot over the years, so many videos will be out of date.

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