JBL 200 Speaker on bar

Building a Universal Remote Dashboard in Home Assistant

Disclosure: Some links on this page are affiliate links. If you purchase through these links, I may earn a small commission at no extra cost to you.

TL;DR: Build a Home Assistant remote dashboard that controls all your TVs and speakers from one place, with circlepad navigation, volume buttons, and automatic device switching—including proper Spotify Connect support.

Home Assistant remote dashboard showing TV control with circlepad navigation and streaming app buttons

Prerequisites

Before building your Home Assistant remote dashboard, you’ll need:

  • Home Assistant (2024.1 or later)
  • HACS (Home Assistant Community Store)
  • Universal Remote Card (installed via HACS)
  • Android TV Remote integration (for TV control)
  • Google Cast or similar integration (for speaker control)
  • Spotify integration (if using Spotify Connect)

The Problem

I have multiple TVs and speakers throughout my home. Each has its own media player entity, and some devices have duplicates from different integrations (Cast, DLNA, Google Home). I wanted a single Home Assistant remote dashboard that shows one remote at a time, always works (even when devices are off), and automatically switches to whatever’s playing.

Initially I tried building remotes with native Home Assistant button grids, but found that the Universal Remote Card provides a much better experience with its circlepad navigation, built-in streaming app icons, and volume controls.

flowchart LR
    A[Device Starts Playing] -->|Triggers| B[Auto-Select Automation]
    B -->|Updates| C[Input Select]
    C -->|Shows| D[Matching Remote Card]

Step 1: Install Universal Remote Card

The Universal Remote Card is the foundation of your Home Assistant remote dashboard. Install it from HACS:

  1. Open HACS in Home Assistant
  2. Go to Frontend → Explore & Download Repositories
  3. Search for “Universal Remote Card”
  4. Click Download
  5. Refresh your browser (Ctrl+F5)

The card supports multiple platforms including Android TV, Roku, Apple TV, and more. For this guide, we’ll focus on Android TV remotes.

Troubleshooting Tip: If you previously installed “Android TV Remote Card” (a different HACS card), you’ll need to remove it first. Having both installed caused the Universal Remote Card to render as an empty box for me. Check HACS → Frontend for any conflicting remote card installations.

Step 2: Create the Device Selector

Create an input_select helper that lists your controllable devices for the remote dashboard:

YAML
input_select:
  remote_device:
    name: Remote Device
    options:
      - "LIVING_ROOM_TV"
      - "BEDROOM_TV"
      - "KITCHEN_SPEAKER"
      - "OFFICE_SPEAKER"
    initial: "LIVING_ROOM_TV"
    icon: mdi:remote-tv

Add your own device names to match your setup.

Step 3: Build the TV Remote Dashboard

The Universal Remote Card requires explicit element listing in the rows configuration. Here’s what I learned building my Home Assistant remote dashboard:

  • Platform: Use Android TV (with a space) for Android TV devices
  • Elements must be explicitly listed: The card won’t show a default layout—you must specify each button in the rows section
  • Custom actions: For buttons like Peacock or backlight toggles, define them in custom_actions
  • Icons: Check both mdi: and phu: (custom-brand-icons) for the best icons

TV Remote Card Configuration

Wrap each remote in a conditional card that shows only when that device is selected:

YAML
# Example TV Remote with Universal Remote Card
- type: conditional
  conditions:
    - condition: state
      entity: input_select.remote_device
      state: LIVING_ROOM_TV
  card:
    type: custom:universal-remote-card
    remote_id: remote.YOUR_TV_REMOTE
    media_player_id: media_player.YOUR_TV
    keyboard_id: remote.YOUR_TV_REMOTE
    platform: Android TV
    autofill_entity_id: true
    haptics: true
    rows:
      - - power
        - backlight
      - circlepad
      - volume_buttons
      - - back
        - keyboard
        - play_pause
        - home
      - - youtube_tv
        - netflix
        - hulu_custom
        - disney
        - peacock
        - youtube
    custom_actions:
      - name: backlight
        type: button
        icon: mdi:television-ambient-light
        tap_action:
          action: perform-action
          perform_action: light.toggle
          target:
            entity_id: light.YOUR_TV_BACKLIGHT
      - name: hulu_custom
        type: button
        icon: phu:hulu
        tap_action:
          action: key
          key: hulu
      - name: youtube_tv
        type: button
        icon: mdi:youtube-tv
        tap_action:
          action: key
          key: youtube_tv
      - name: peacock
        type: button
        icon: phu:peacock
        tap_action:
          action: perform-action
          perform_action: remote.turn_on
          target:
            entity_id: remote.YOUR_TV_REMOTE
          data:
            activity: com.peacocktv.peacockandroid

Icon Tips for Your Remote Dashboard

Not all streaming service icons are available in the default Material Design Icons (mdi). I found better icons in the custom-brand-icons pack (phu prefix):

  • phu:hulu – Better than mdi:hulu
  • phu:peacock – No mdi:peacock exists
  • mdi:youtube-tv – Works well for YouTube TV
  • mdi:netflix, mdi:youtube, mdi:disney – Built-in defaults work fine

Install custom-brand-icons via HACS if you need more brand icons.

Adding More Streaming Services

To add buttons for other streaming apps to your remote dashboard:

  1. Check built-in keys: The Universal Remote Card has built-in support for many apps. Check the card documentation for a list of supported keys like netflix, hulu, disney, amazon, spotify, etc.
  2. Find the package name: For apps not built-in, you need the Android package name. On your TV, go to Settings → Apps and find the app. The package name is usually shown (e.g., com.peacocktv.peacockandroid).
  3. Use remote.turn_on: Create a custom action that launches the app by package name.
  4. Find icons: Search MDI icons first, then check custom-brand-icons (phu) for brand-specific icons.

Example custom action for an app not built into the card:

YAML
custom_actions:
  - name: max
    type: button
    icon: phu:max  # or mdi:alpha-m-box for generic
    tap_action:
      action: perform-action
      perform_action: remote.turn_on
      target:
        entity_id: remote.YOUR_TV_REMOTE
      data:
        activity: com.wbd.stream  # HBO Max package name

TVs Without Backlights

For TVs without LED backlights, simply remove the backlight from the first row:

YAML
rows:
  - power  # Single element, not a row array
  - circlepad
  - volume_buttons
  - - back
    - keyboard
    - play_pause
    - home
  - - youtube_tv
    - netflix
    # ... rest of apps

Step 4: Build the Speaker Remote

Speaker remotes for your Home Assistant remote dashboard are simpler—just media controls using native cards. I use volume buttons instead of a slider for better touch accuracy on mobile:

Home Assistant remote dashboard speaker view displaying Spotify media info and volume controls
YAML
# Example Speaker Remote
- type: conditional
  conditions:
    - condition: state
      entity: input_select.remote_device
      state: KITCHEN_SPEAKER
  card:
    type: vertical-stack
    cards:
      - type: media-control
        entity: media_player.YOUR_SPEAKER
      - square: false
        columns: 3
        type: grid
        cards:
          - type: button
            icon: mdi:skip-previous
            tap_action:
              action: perform-action
              perform_action: media_player.media_previous_track
              target:
                entity_id: media_player.YOUR_SPEAKER
          - type: button
            icon: mdi:play-pause
            tap_action:
              action: perform-action
              perform_action: media_player.media_play_pause
              target:
                entity_id: media_player.YOUR_SPEAKER
          - type: button
            icon: mdi:skip-next
            tap_action:
              action: perform-action
              perform_action: media_player.media_next_track
              target:
                entity_id: media_player.YOUR_SPEAKER
      - square: false
        columns: 3
        type: grid
        cards:
          - type: button
            icon: mdi:volume-minus
            tap_action:
              action: perform-action
              perform_action: media_player.volume_down
              target:
                entity_id: media_player.YOUR_SPEAKER
          - type: button
            icon: mdi:volume-off
            tap_action:
              action: perform-action
              perform_action: media_player.volume_mute
              data:
                is_volume_muted: true
              target:
                entity_id: media_player.YOUR_SPEAKER
          - type: button
            icon: mdi:volume-plus
            tap_action:
              action: perform-action
              perform_action: media_player.volume_up
              target:
                entity_id: media_player.YOUR_SPEAKER

Step 5: Add Auto-Selection to Your Remote Dashboard

Create a template sensor that detects which device is currently playing:

Important – Cast vs Android TV Entities: For TVs, you may have multiple media_player entities from different integrations. The Cast entity (e.g., media_player.living_room_tv) reports playing state with full media metadata (title, app, artist). The Android TV entity (e.g., media_player.living_room_tv_2) often only shows on/off without media info. Use Cast entities for detection sensors and Android TV entities for remote control.

YAML
template:
  - sensor:
      - name: "Remote Active Device"
        unique_id: remote_active_device
        state: >
          {% set devices = [
            ('LIVING_ROOM_TV', 'media_player.YOUR_LIVING_ROOM_TV'),
            ('BEDROOM_TV', 'media_player.YOUR_BEDROOM_TV'),
            ('KITCHEN_SPEAKER', 'media_player.YOUR_KITCHEN_SPEAKER'),
            ('OFFICE_SPEAKER', 'media_player.YOUR_OFFICE_SPEAKER')
          ] %}
          {% set active_states = ['playing', 'paused', 'buffering'] %}
          {% set playing = namespace(device=none) %}
          {% for name, entity in devices %}
            {% set state = states(entity) %}
            {% set has_media = state_attr(entity, 'media_title') not in [none, '', 'unknown'] %}
            {% if playing.device is none %}
              {% if state in active_states %}
                {% set playing.device = name %}
              {% elif state == 'idle' and has_media %}
                {% set playing.device = name %}
              {% endif %}
            {% endif %}
          {% endfor %}
          {{ playing.device if playing.device else 'none' }}
        icon: mdi:play-circle

Then create an automation to switch the remote dashboard when a device starts playing:

YAML
automation:
  - id: remote_auto_select_playing_device
    alias: 'Remote: Auto-Select Playing Device'
    trigger:
      - platform: state
        entity_id: sensor.remote_active_device
    condition:
      - condition: template
        value_template: "{{ trigger.to_state.state != 'none' }}"
    action:
      - service: input_select.select_option
        target:
          entity_id: input_select.remote_device
        data:
          option: "{{ trigger.to_state.state }}"

Handling Spotify Connect

I discovered an issue with my Home Assistant remote dashboard: when playing Spotify via Spotify Connect to a Google Cast speaker, the Cast entity often shows off even while music is playing. The media-control card displays nothing because the speaker entity has no media information.

The Spotify media player entity (media_player.spotify_username) has all the information—current track, artist, album art—and a source attribute showing which speaker it’s playing to. The challenge is getting the dashboard to show the Spotify card when appropriate.

Why Attribute Conditions Don’t Work

My first attempt was using a conditional card with an attribute condition:

YAML
# This does NOT work!
- type: conditional
  conditions:
    - condition: state
      entity: media_player.spotify_andy
      attribute: source
      state: "Office speaker"
  card:
    type: media-control
    entity: media_player.spotify_andy

This causes configuration errors. After researching, I found that Lovelace conditional cards only support limited condition types: state, numeric_state, screen, user, location, time, and and/or. They don’t support attribute conditions or template conditions.

The Solution: Binary Sensors

The workaround is to create template binary sensors that expose the Spotify source check, then use simple state conditions in the dashboard. Add these to your configuration.yaml:

YAML
template:
  - binary_sensor:
      - name: "Spotify Playing on Kitchen Speaker"
        unique_id: spotify_playing_kitchen_speaker
        state: >
          {{ is_state('media_player.spotify_YOUR_USERNAME', 'playing') and
             state_attr('media_player.spotify_YOUR_USERNAME', 'source') == 'Kitchen speaker' }}
        icon: mdi:spotify
      - name: "Spotify Playing on Office Speaker"
        unique_id: spotify_playing_office_speaker
        state: >
          {{ is_state('media_player.spotify_YOUR_USERNAME', 'playing') and
             state_attr('media_player.spotify_YOUR_USERNAME', 'source') == 'Office speaker' }}
        icon: mdi:spotify
      # Add one for each speaker...

Important: The source name must match exactly what Spotify reports. Check Developer Tools → States → media_player.spotify_YOUR_USERNAME while playing on each speaker to get the exact source names.

Future Enhancement: This approach currently works with a single Spotify account. If you need to support multiple Spotify accounts playing on different speakers simultaneously, you would replace these binary sensors with template sensors that return which Spotify entity is playing on each speaker. The dashboard would then use a single conditional per speaker with the returned entity. This scales better for 2-3 accounts.

Updated Speaker Remote with Spotify Support

Now update your speaker remotes to show the Spotify card when Spotify is playing:

YAML
# Speaker with Spotify-aware media display
- type: conditional
  conditions:
    - condition: state
      entity: input_select.remote_device
      state: "OFFICE_SPEAKER"
  card:
    type: vertical-stack
    cards:
      # Show Spotify card when playing on this speaker
      - type: conditional
        conditions:
          - condition: state
            entity: binary_sensor.spotify_playing_on_office_speaker
            state: "on"
        card:
          type: media-control
          entity: media_player.spotify_YOUR_USERNAME
      # Show speaker card when Spotify is NOT playing here
      - type: conditional
        conditions:
          - condition: state
            entity: binary_sensor.spotify_playing_on_office_speaker
            state: "off"
        card:
          type: media-control
          entity: media_player.YOUR_OFFICE_SPEAKER
      # Playback controls
      - square: false
        columns: 3
        type: grid
        cards:
          - type: button
            icon: mdi:skip-previous
            tap_action:
              action: perform-action
              perform_action: media_player.media_previous_track
              target:
                entity_id: media_player.YOUR_OFFICE_SPEAKER
          - type: button
            icon: mdi:play-pause
            tap_action:
              action: perform-action
              perform_action: media_player.media_play_pause
              target:
                entity_id: media_player.YOUR_OFFICE_SPEAKER
          - type: button
            icon: mdi:skip-next
            tap_action:
              action: perform-action
              perform_action: media_player.media_next_track
              target:
                entity_id: media_player.YOUR_OFFICE_SPEAKER
      # Volume controls
      - square: false
        columns: 3
        type: grid
        cards:
          - type: button
            icon: mdi:volume-minus
            tap_action:
              action: perform-action
              perform_action: media_player.volume_down
              target:
                entity_id: media_player.YOUR_OFFICE_SPEAKER
          - type: button
            icon: mdi:volume-off
            tap_action:
              action: perform-action
              perform_action: media_player.volume_mute
              data:
                is_volume_muted: true
              target:
                entity_id: media_player.YOUR_OFFICE_SPEAKER
          - type: button
            icon: mdi:volume-plus
            tap_action:
              action: perform-action
              perform_action: media_player.volume_up
              target:
                entity_id: media_player.YOUR_OFFICE_SPEAKER

Key points:

  • The binary sensor condition shows the Spotify media-control card when that sensor is “on”
  • When Spotify isn’t playing on this speaker, the regular speaker card shows instead
  • Playback and volume buttons always target the speaker entity directly

For this setup, I use JBL Authentics speakers throughout my home. They integrate seamlessly with Home Assistant through both Google Cast and Google Home integrations, and work great with Spotify Connect.

JBL Authentics 200 speaker for Home Assistant remote dashboard
JBL Authentics speakers combine great sound with classic styling

Why I like them:

  • Great sound quality with classic retro aesthetics
  • Built-in Google Assistant and Alexa (use either or neither)
  • Native Spotify Connect support
  • Show up as Cast devices in Home Assistant
  • The Authentics 300 has a built-in battery—I move it outside when we’re on the deck

I have the JBL Authentics 200 in my office and the JBL Authentics 300 in the dining area. Both have been reliable for over a year.

Your Complete Home Assistant Remote Dashboard

Your Home Assistant remote dashboard now:

  • ✅ Shows one remote at a time via dropdown selector
  • ✅ TV remotes include circlepad navigation, volume buttons, and streaming app shortcuts
  • ✅ Speaker remotes include playback and volume button controls
  • ✅ Keyboard button for text entry on Android TVs
  • ✅ Optional backlight toggle for TVs with LED strips
  • ✅ Remotes always visible, even when devices are off
  • ✅ Auto-switches to any device that starts playing
  • ✅ Shows Spotify media info via binary sensor workaround

Useful Links:
GitHub Config Files – Sanitized YAML configs for this project
Universal Remote Card – The HACS card used for TV remotes
Custom Brand Icons – Additional icons including phu:hulu and phu:peacock
Material Design Icons – Search for mdi: icons
Home Assistant Conditional Cards – Official documentation on supported conditions

Hardware:
Google TV Streamer 4K: Amazon
JBL Authentics 200: Amazon
JBL Authentics 300: Amazon

Related Posts:
Monitoring Energy Usage in Home Assistant – Track your smart home power consumption

Similar Posts

Leave a Reply