Getting nested value

Hey all,
not sure how to grab nested values out from a attribute, which has multiple objects in it.

The sensor.next_train_to_btn has these results, which are in the “next trains” attribute.

How can i pull out the value for the 2nd and 3rd trains in this list?

so
sensor.next train = scheduled time of 13:56
sensor.following train = scheduled time of 14:15

{
  "station_code": "WRH",
  "calling_at": "BTN",
  "next_trains": [
    {
      "origin_name": "Southampton Central",
      "destination_name": "Brighton",
      "status": "ON TIME",
      "scheduled": "13:56",
      "estimated": "13:56",
      "platform": "2",
      "operator_name": "Southern"
    },
    {
      "origin_name": "West Worthing",
      "destination_name": "Brighton",
      "status": "ON TIME",
      "scheduled": "14:15",
      "estimated": "14:15",
      "platform": "2",
      "operator_name": "Southern"
    },
    {
      "origin_name": "West Worthing",
      "destination_name": "Brighton",
      "status": "ON TIME",
      "scheduled": "15:15",
      "estimated": "15:15",
      "platform": "2",
      "operator_name": "Southern"
    },
  ],
  "unit_of_measurement": "min",
  "friendly_name": "Next train to BTN",
  "icon": "mdi:train"
}

Just to clarify, you have sensor.next_train_to_btn and it contains an attribute named next_trains which contains a JSON dict as shown in your post above?

If what I said is true, then if you paste this template into the Template Editor, it should report the time of the next train:

{{ state_attr('sensor.next_train_to_btn', 'next_trains').next_trains[0].scheduled }}

To get the following train, change the 0 in the template above to 1.

1 Like

hey, thanks for the quick reply.

all assumptions are correct (sorry for not being clear)

i still get this error however:

Error rendering template: UndefinedError: ‘list object’ has no attribute ‘next_trains’

1 Like

this seems to have done it. thanks for the help to get me there!!

{{states.sensor.next_train_to_btn.attributes.next_trains[1].scheduled}}

Actually, no …

The following two forms produce the same result:

states.sensor.test.attributes.something

state_attr('sensor.test', 'something')

The sole difference between the two is the second form is a function (and provides a benefit that isn’t germane to this example).

The solution I provided you does work. I tested it by creating a sensor with an attribute called next_trains containing the same JSON data you posted.

When tested in the Template Editor, both methods of extracting the attribute’s value work correctly:

The information you wish to access is contained in a list which is zero-based (first item in the list is the zeroth item). To get the first item we use .next_trains[0]. To get the second one we use .next_trains[1].

So the post you’ve tagged as ‘Solution’ is not functionally different (nor somehow more correct) than what I offered you.


The sole reason I’m explaining this is because I’ve seen this happen many times before.

  • Someone asks how to add one and one.
  • I reply: 1+1=2
  • They reply: That didn’t work for me but 1 + 1 = 2 did so that’s the solution!
  • I say: Actually, no …
4 Likes

thanks! totally understand, so can you advise how i can get your solution to work, as it still gives me that error sorry?

Absolutely.

I noticed a difference in the screenshot of your sensor and mine. Yours has more attributes and next_trains is displayed as [object object],[object Object],etc. That’s because it wasn’t clear (at least to me) what the JSON data you displayed in your first actually represents. Based on your description, I interpreted it to be the value held within the next_trains attribute (because that’s the attribute you were attempting to parse).

In fact, that’s the payload your sensor receives (potentially via its json_attributes_topic) and converts into attributes. That definitely changes how one should extract data from the next_trains attribute!

I’ve adapted my test sensor accordingly, so now it appears more like yours does in the States page:

As a consequence, the template to extract the first (zeroth) item is simplified. Here are both forms:

If you need the second item, use [1].scheduled

Paste this into your Template Editor and both forms should produce the same result:

{{ state_attr('sensor.next_train_to_btn', 'next_trains')[1].scheduled }}

{{ states.sensor.next_train_to_btn.attributes.next_trains[1].scheduled }}

Hope this helps clarify how to extract values from a JSON dict.

3 Likes

awesome, thank you!

1 Like

sorry, just to clarify, what is the difference between each? pros/cons?

  • The first one uses the state_attr function to acquire the entity’s attribute value. If either the entity or the attribute don’t exist, the function return None.

  • The second one uses properties of the states object. As long as you have at least one sensor defined then states.sensor is valid. If you’ve defined a sensor.something then states.sensor.something is valid. Etc, etc. If you do not have a sensor.something then states.sensor.something.attributes.whatever will fail and result in an error (i.e. it doesn’t report None, it just fails).

So the preference is to use the states and state_attr functions because they are more tolerant of non-existent entities (and attributes) and will report None. I also find them easier to read than the other form.

HOWEVER, in this particular case it makes no difference which form we use! Why? Because we are appending [1].scheduled to the output of the state_attr function:

state_attr('sensor.next_train_to_btn', 'next_trains')[1].scheduled

So if the output of the function is None, the template will attempt to get [1].scheduled from None and that, of course, will fail with an error.

2 Likes

Hi. I have similar timetable sensor giving info about trams departures. I would like to process it and get a new sensor showing next tram with number 9 or 49 which departures not earlier than 15 minutes from now. I’ve managed to create sensor showing trams 9 or 49 but my code is cumbersome and doesn’t involve checking if tram will departure above 15 minutes from now. I would appreciate any help.

Sensor mpk_kr_kabel_tram_departure output:

My Template:

{% set odjazd4 = state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[4]['line'] %}
{% set odjazd5 = state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[5]['line'] %}
{% set odjazd6 = state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[6]['line'] %}
{% set odjazd7 = state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[7]['line'] %}
{% set odjazd8 = state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[8]['line'] %}
{% set odjazd9 = state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[9]['line'] %}

{% if odjazd4 == '9' or odjazd4 == '49' %}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[4]['departure'] }}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[4]['line'] }} 
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[4]['time_to_departure'] }}
{% endif %}

{% if odjazd5 == '9' or odjazd5 == '49' %}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[5]['line'] }}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[5]['departure'] }}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[5]['time_to_departure'] }}
{% endif %}

{% if odjazd6 == '9' or odjazd6 == '49'%}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[6]['line'] }}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[6]['departure'] }}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[6]['time_to_departure'] }}
{% endif %}

{% if odjazd7 == '9' or odjazd7 == '49'%}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[7]['line'] }}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[7]['departure'] }}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[7]['time_to_departure'] }}
{% endif %}

{% if odjazd8 == '9' or odjazd8 == '49'%}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[8]['line'] }}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[8]['departure'] }}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[8]['time_to_departure'] }}
{% endif %}

{% if odjazd9 == '9' or odjazd9 == '49'%}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[9]['line'] }}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[9]['departure'] }}
{{ state_attr('sensor.mpk_kr_kabel_tram_departure', 'list')[9]['time_to_departure'] }}
{% endif %}

Hello everyone, I have a similar issue, only the problem is I cannot extract the value from "converter_factor_kwh/m3" which has a slash / in it.
Here is my problem:


If anyone knows a way?
Thanks a lot :slight_smile:

Just bringing this thread back to life.

I have this entity:-

calendar.rental_control_spare_room

message: Spare Room Reserved
all_day: false
start_time: '2023-02-08 11:00:00'
end_time: '2023-02-09 11:00:00'
location: ''
description: >-
  Reservation URL:
  https://www.airbnb.com/hosting/reservations/details/XYZ

  Phone Number (Last 4 Digits): 1234
friendly_name: Rental Control Green Room

How would I go about extracting the Reservation URL out into a template?

1 Like