Complex data structure with iterator

I would like to create a radio-station switch for my Sonos speaker consiting of one input select and two buttons. That’s easy, but now comes the beef.

I’d like to use a data structure which has for each station a name, an url and up to three options. In JSON it would look like this one:

{
  "stations": [
    {
      "name": "Station One",
      "url": "https://one.example.com",
      "inital_volume": 120
    },
    {
      "name": "Radio two",
      "url": "https://two.example.com",
      "inital_volume": 100
    },
    {
      "name": "LiveTalk",
      "url": "https://three.example.com",
      "inital_volume": 110
    }
  ]
}

What I had not found an solution for is a way, how to read and access the data structure from within an automation or a script.

I’m looking for:

  1. create a drop down the data structure. I know about “input select - set_options”, but don’t know how to access my data object from within an automation and how to iterate on it.
  2. create a script to fetch one station by a nummerical index. Having this I can create a next station / previous station switch and push the URL to my Sonos Speaker

And yes, I know about Media Card, Media Player, Radio Player … They do not offer, what I’m looking at. And I would use my own data structure, not the favorites from my Sonos.

Any suggestion on this?

Hope “Configuration” is the right place for this, looks like almost any similar discussion happend here.

Cheers, Christian

Things you might want to consider:

Custom jinja file

Template Select

@Didgeridrew thanks but I do not understand how these two may answer my questions.

What I tried in the meantime to fetch the data into a sensor:


sensor:
  - platform: file
    name: radiostations
    file_path: ./my_radiostations.json
    value_template: true
  - platform: command_line
    name: radios
    scan_interval: 20
    command: "jq '.stations' ./my_radiostations.json"
    value_template: "OK"
    json_attributes:
      - name
      - url

Both are not working.

Try the command line one with a simple echo or cat, and:

json_attributes:
  - stations

thank you @Troon, I changed it to

sensor:
  - platform: command_line
    name: radios
    scan_interval: 20
    command: "cat /root/homeassistant/my_radiostations.json"
    json_attributes:
      - stations

and tried a lot of variants, even with a very basic json, without json_attributes and whatsover.

I would expect to find a sensor.radios in the entity list view or to access him by {{ sensor.radios }} in a message I send to the logfile. But what I get is a error message of

Template variable error: ‘sensor’ is undefined when rendering ‘radio is {{ sensor.radios }}’

Ah, the command line syntax has changed. See the docs:

Needs to be:

command_line:
  - sensor:
      name: radios
      scan_interval: 20
      command: "cat /root/homeassistant/my_radiostations.json"
      json_attributes:
        - stations

and will need a restart.

Then it’ll be:

{{ states.sensor.radios }}

I think they key point you’re missing is that jinja variables are just Python, and can be lists or dicts. So, you can define your stations as a variable:

{% set stations = [
    {
      "name": "Station One",
      "url": "https://one.example.com",
      "inital_volume": 120
    },
    {
      "name": "Radio two",
      "url": "https://two.example.com",
      "inital_volume": 100
    },
    {
      "name": "LiveTalk",
      "url": "https://three.example.com",
      "inital_volume": 110
    }
  ]
%}

{{ stations | map(attribute="name") | list }}

yields

[
  "Station One",
  "Radio two",
  "LiveTalk"
]

Yes, that’s ideal if they never change, and what the earlier suggestion of the reusable template was about.

@ImSorryButWho That sounds like a promising approach. But unfortunately I do not understand how to create such variables. Do I have to use a template.yaml and create a sensor in it which will held the variable? And is such variable afterwards available in scripts or automations?

@Troon What I completely oversaw in the documentation is the information that reloading just the yaml does not instanciate a sensor. Some has to restart HA from the scratch. Thanks for the hint :slight_smile:

I was not able to load any data from the command line. After a lot of tries/restarts I decides to use a rest endpoint, put my json file into ./config/www/ directory. Now I can load it.

sensor:
  - platform: rest
    resource: https://homeassistant.local:8123/local/my_radiostations.json
    name: radios
    unique_id: 42ce0e4c-0f7a-4c6e-9bf6-55377b7a3c14
    verify_ssl: false
    value_template: "{{ value_json['stations'] }}"
    scan_interval: 120

Now {{ states('sensor.radios') }} gives me a string:

[{'name': 'Station One', 'url': 'https://one.example.com', 'inital_volume': 120}, {'name': 'Radio two', 'url': 'https://two.example.com', 'inital_volume': 100}, {'name': 'LiveTalk', 'url': 'https://three.example.com', 'inital_volume': 110}]

And if I echo {{ states.sensor.radios }} I get a

 <template TemplateState(<state sensor.radios=[{'name': 'Station One', 'url': 'https://one.example.com', 'inital_volume': 120}, {'name': 'Radio two', 'url': 'https://two.example.com', 'inital_volume': 100}, {'name': 'LiveTalk', 'url': 'https://three.example.com', 'inital_volume': 110}]; friendly_name=radios

My problem is, I find tons of description on how to get one value from a json into a sensor (“value_template”) but very little on how to deal with a data structure like mine.

Right. So, you can use templates all over Home Assistant: they’re basically a way of injecting programmability all over the place. Essentially, any place that takes a value can also take a template, like the data section of a service call.

Each template runs in it’s own little environment, which means that variables you define are only defined within the template environment they’re running in, not globally. But that’s okay - that’s where the “Custom jinja file” that @Didgeridrew mentioned comes in. You can define a macro which returns the value for the stations variable.

Somewhat annoyingly, macros only return strings, not full data types. But there’s a from_json filter that will rehydrate it.

So, in custom_template/stations.jinja include your definition:

{% macro stations_data() %}
[
    {
      "name": "Station One",
      "url": "https://one.example.com",
      "inital_volume": 120
    },
    {
      "name": "Radio two",
      "url": "https://two.example.com",
      "inital_volume": 100
    },
    {
      "name": "LiveTalk",
      "url": "https://three.example.com",
      "inital_volume": 110
    }
  ]
{% endmacro %}

Then, in any template where you want to use them, do this:

{% from 'stations.jinja' import stations_data %}
{% set stations = (stations_data() | from_json) %}
{{ stations | map(attribute="name") | list }} 
1 Like

Because states are limited to 255 characters, large data structures are loaded into attributes, and replicate the data structure there.

Add:

json_attributes:
  - stations

to your Rest sensor and reload it.

Here’s the documentation you wanted:

@ImSorryButWho thank for the solution. I’m able to work with this :slight_smile:

ATM I do the from / Import / Set in one script but would like to re-use the same macro in at least five more scripts. Do you know any approach how to make stations a global variable or at least global in one yaml file?

Hmm, even using the variable in a script does not work. Had revied >20 homeassistant-config projects on github but got no examples.

How to assign a value from a jinja template in a script? I use the following code

radio_station_next:
  mode: single
  sequence:
    - service: input_number.set_value
      target:
        entity_id: input_number.number_radio_current_station
      data:
        value: >-
          {% from 'stations.jinja' import stations_data %}
          {% set stations = (stations_data() | from_json) %}
          '{{ stations | length  | int}}'

Remove the quotes from the final line.