Dynamic Radio Station List in Home Assistant Dashboard

Hi there

Goal

The objective is to create a dynamically populated list of radio stations in the Home Assistant dashboard. Each radio station can be played using the Music Assistant integration, and its corresponding logo will be displayed as a button. Instead of manually adding each radio station as a button beforehand, I aim to create a solution that automatically generates buttons based on radio station logos placed in a specific folder.


What I Have So Far

  1. Folder Sensor I am using the folder platform sensor to detect and list all the image files (e.g., .jpg) in the /config/www/radio directory. Here is the configuration for the folder sensor:
sensor:
  - platform: folder
    folder: "/config/www/radio"
    filter: "*"

This sensor retrieves a list of image files representing the radio station logos.

  1. Template to Iterate Over Image Files The following Jinja2 template iterates over the file_list attribute of the folder sensor (sensor.radio). It generates a YAML structure for each radio station, including its name (derived from the image filename), the image URL, and a button configuration to play the station using the music_assistant.play_media service.
{%- set files = namespace(value=[]) -%}
{%- for item in state_attr('sensor.radio','file_list') -%}
{%- set files.value = files.value + [item] -%}
{%- set name =  item.replace('/config/www/radio/', '').replace('.jpg','').replace('.png','') -%}
{%- set image = item.replace('/config/www', '/local')-%}
- type: picture
  name: {{ name }}
  image: {{image}}
  tap_action:
    action: call-service
    service: music_assistant.play_media
    service_data:
      entity_id: media_player.dt
      media_id: {{ name }}
      media_type: radio
{% endfor %}
  1. Template Output Below is an example of the YAML output generated by the template for two radio stations:
- type: picture
  name: 1.FM - Chillout Lounge Radio
  image: /config/www/radio/1.FM - Chillout Lounge Radio.jpg
  tap_action:
    action: call-service
    service: music_assistant.play_media
    service_data:
      media_id: 1.FM - Chillout Lounge Radio
      media_type: radio
- type: picture
  name: Radio Swiss Pop
  image: /config/www/radio/Radio Swiss Pop.jpg
  tap_action:
    action: call-service
    service: music_assistant.play_media
    service_data:
      media_id: Radio Swiss Pop
      media_type: radio

Question

How can I dynamically generate and display these picture buttons on the Home Assistant dashboard? Ideally, the solution should create these buttons automatically without requiring manual configuration whenever new radio station logos are added to the folder.

I’m looking forward solving the last part with the help of the community.

Thanks

I’ve found a way to dynamically generate the radio station list with thelp of the custom:auto-entities and custom:layout-card HACS Integrations.

type: custom:auto-entities
card:
  type: custom:layout-card
  layout_type: custom:grid-layout
  layout:
    grid-template-columns: auto auto auto auto auto auto
    grid-template-rows: auto
filter:
  template: >
    {%- set ns = namespace(results = []) -%} {%- set files = namespace(value=[])
    -%} {%- for item in state_attr('sensor.radio','file_list') | sort -%} {%-
    set files.value = files.value + [item] -%} {%- set name = 
    item.replace('/config/www/radio/', '').replace('.jpg','').replace('.png','')
    -%} {%- set image = item.replace('/config/www', '/local')-%} {%- set
    ns.results = ns.results + [
      {
        "image": image,
        "alt_text": name,
        "type": "picture",
        "tap_action": {
          "action": "perform-action",
          "perform_action": "script.system_tune_onkyo_radio",
          "data": {
            "media_id": name
          }
        },
        "styles": {
          "card": {
            "margin": "1px",
            "padding": "1px"
          }
        }
      }
    ]-%} {% endfor %} {{ ns.results }}

This is pretty much what I was looking for and I’m pleased to have found the solution after many hours of seeking forums and blogs.

The template evaulation takes a noticeable time to render that one could even see when the dashoard gets loaded.

That’s why I prefer to copy and paste the template output manually which loads without any delay.

Is there anything I could do to make the template evaluate faster?

Hi @ChrisRichner
Thanks for posting your solution. I’m trying to replicate it without success. Here’s what I did.

  1. Iinstalled auto-entities and layout-card from the HACS store.
  2. Created radio folder in /config/www > /config/www/radio
  3. From https://www.radio-browser.info I found the radio station of interest and looked up the exact name “Radio Opole”.
  4. Found a logo of the radio station and uploaded it to /config/www/radio as Radio Opole.png
  5. In Dashboard “Add Card” > “Manual” and pasted the code from post #2 above.
  6. Click “Save” and nothing happens.

Not sure what I’m doing wrong. Would you also mind sharing a screenshot of how the card should look in the dashboard?

Hi @art123

Sure, in edit Mode it looks like this

First Version

type: vertical-stack
cards:
  - type: horizontal-stack
    cards:
      - type: custom:auto-entities
        card:
          type: custom:layout-card
          show_header_toggle: false
        filter:
          template: >
            {%- set ns = namespace(results = []) -%} {%- set files =
            namespace(value=[]) -%} {%- for item in
            state_attr('sensor.radio','file_list') | sort -%} {%- set
            files.value = files.value + [item] -%} {%- set name = 
            item.replace('/config/www/radio/',
            '').replace('.jpg','').replace('.png','') -%} {%- set image =
            item.replace('/config/www', '/local')-%} {%- set ns.results =
            ns.results + [
              {
                "image": image,
                "alt_text": name,
                "type": "picture",
                "tap_action": {
                  "action": "perform-action",
                  "perform_action": "script.system_tune_onkyo_radio",
                  "data": {
                    "media_id": name
                  }
                }        
              }
            ]-%} {% endfor %} {{ ns.results }}

Second Version

type: custom:auto-entities
card:
  type: custom:layout-card
  layout_type: custom:grid-layout
  layout:
    grid-template-columns: auto auto auto auto auto auto
    grid-template-rows: auto
filter:
  template: >
    {%- set ns = namespace(results = []) -%} {%- set files = namespace(value=[])
    -%} {%- for item in state_attr('sensor.radio','file_list') | sort -%} {%-
    set files.value = files.value + [item] -%} {%- set name = 
    item.replace('/config/www/radio/', '').replace('.jpg','').replace('.png','')
    -%} {%- set image = item.replace('/config/www', '/local')-%} {%- set
    ns.results = ns.results + [
      {
        "image": image,
        "alt_text": name,
        "type": "picture",
        "tap_action": {
          "action": "perform-action",
          "perform_action": "script.system_tune_onkyo_radio",
          "data": {
            "media_id": name
          }
        },
        "styles": {
          "card": {
            "margin": "1px",
            "padding": "1px"
          }
        }
      }
    ]-%} {% endfor %} {{ ns.results }}

to troubleshoot I would recommend to

  1. check logs to see if you find any issues
  2. go to Developer tools/States and check if the folder sensor has the attribute with the expected file list, you can find the sensor by name sensor.radio
  3. go to Developer tools/Template and copy the template part to see if it works correctly by generating the expected dashboard config.

I’m interested in your experience with the rendering times when the dashboard loads, is it acceptable for you? How many radio stations do you have?

Let me know if you need any further assistance, I’m glad to help.

1 Like

Hi, @ChrisRichner

Thank you for the write up and help with this. I’ve made some progress.

  • I tried copying the code as is and still nothing showed up.
  • I looked in the logs (Settings > System > Logs > Selected Home Assistant Core in the top-right). I got lost there, not knowing what to look for. I did search for “onkyo” because that was a unique word. There was an error and I’m providing the log at the bottom for reference.
  • I re-read your post and realized that I need to add the sensor.radio to the configuration.yaml file > verified configruation.yaml file in developer tools > Re-started Home Assistant > now the icons populate :slight_smile:
sensor:
  - platform: folder
    folder: "/config/www/radio"
    filter: "*"

When I click on the radio button I get an error relating to "perform_action": "script.system_tune_onkyo_radio", Two follow-up questions:

  1. What is this script?
    • I have a feeling that in this script I would specify which speaker to play the radio station on?
  2. How are you able to pull the radio station web address from the logo.png files? Is that what the script does? Is it possible to share it?

2024-12-30_18-03 radio buttons

Log output for reference

  • Since I re-started Home Assistant I can’t find this error any longer. It looks like logs aren’t kept after reboot.
2024-12-30 17:14:13.470 ERROR (MainThread) [homeassistant.helpers.event] Error while processing template: Template<template=({%- set ns = namespace(results = []) -%} {%- set files = namespace(value=[]) -%} {%- for item in state_attr('sensor.radio','file_list') | sort -%} {%- set files.value = files.value + [item] -%} {%- set name =  item.replace('/config/www/radio/', '').replace('.jpg','').replace('.png','') -%} {%- set image = item.replace('/config/www', '/local')-%} {%- set ns.results = ns.results + [
  {
    "image": image,
    "alt_text": name,
    "type": "picture",
    "tap_action": {
      "action": "perform-action",
      "perform_action": "script.system_tune_onkyo_radio",
      "data": {
        "media_id": name
      }
    }        
  }
]-%} {% endfor %} {{ ns.results }}) renders=110>
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 631, in async_render
    render_result = _render_with_context(self.template, compiled, **kwargs)
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 2733, in _render_with_context
    return template.render(**kwargs)
           ~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/usr/local/lib/python3.13/site-packages/jinja2/environment.py", line 1304, in render
    self.environment.handle_exception()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/usr/local/lib/python3.13/site-packages/jinja2/environment.py", line 939, in handle_exception
    raise rewrite_traceback_stack(source=source)
  File "<template>", line 1, in top-level template code
  File "/usr/local/lib/python3.13/site-packages/jinja2/filters.py", line 437, in do_sort
    return sorted(value, key=key_func, reverse=reverse)
TypeError: 'NoneType' object is not iterable

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 748, in async_render_to_info
    render_info._result = self.async_render(  # noqa: SLF001
                          ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
        variables, strict=strict, log_fn=log_fn, **kwargs
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 633, in async_render
    raise TemplateError(err) from err
homeassistant.exceptions.TemplateError: TypeError: 'NoneType' object is not iterable

Hi @art123,

I’m glad to hear you solved the issue! :+1:

I’ve recently started using scripts to make my setup more robust by separating actions from triggers. In this case, the triggers are the Radio Buttons, and the actions define what happens when a button is clicked.

The script leverages the Music Assistant Integration media player by calling play_media with the Radio Station Name as media_id and media_type: radio. In my setup, this turns on the Onkyo AVR and streams the selected Radio Station via AirPlay.

I’ve only been using this approach for a few days. Previously, I relied on the Net-TuneIn functionality on the Onkyo AVR for several years. However, all my favorite stations were lost overnight, so I came up with this new setup. :blush:

Here’s the script in case you’d like to replicate my setup:

alias: system_tune_onkyo_radio
variables:
  volume: 0.37
sequence:
  - action: music_assistant.play_media
    data:
      media_id: "{{ media_id }}"
      media_type: radio
    target:
      entity_id: media_player.onkyo_2
  - delay:
      hours: 0
      minutes: 0
      seconds: 10
      milliseconds: 0
    enabled: true
  - data:
      entity_id: media_player.onkyo
      volume_level: "{{ volume }}"
    enabled: true
    action: media_player.volume_set
mode: single
icon: mdi:radio
fields:
  media_id:
    selector:
      text: null
    name: media_Id
    description: >-
      URI or name of the item you want to play. Specify a list if you want to
      play/enqueue multiple items.
description: ""

Did you already install the Music Assistant Addon + Music Assistant Integration?

In the Settings of Music Assistant I’ve installed the RadioBrowser Music Provider and Airplay under Player Providers.