Build a Reliable Music Assistant ESP32 Media Controller: Complete HACS Setup

music-assistant-esp32-media-controller.md
title Build a Reliable Music Assistant ESP32 Media Controller: Complete HACS Setup
date
author VahaC
read 6 min read
category Smart home
tags #EspHome #HomeAssistant #SmartHome
Music Assistant ESP32 media controller

A wall panel that only shows what is playing is fine — until you want the live queue, your playlists and the room’s lights on the same screen. This project is a Music Assistant ESP32 media controller packaged so anyone can install it: a HACS-ready Home Assistant integration plus a maintained ESPHome package for the ESP32-S3-4848S040. 🎵

The Music Assistant ESP32 media controller shows album art, a live progress arc, a bounded queue, playlists and four room controls on a 480×480 touchscreen — and every installation-specific value is picked in the Home Assistant UI, not hardcoded in firmware. Full source: ha-media-controller


What This Music Assistant ESP32 Media Controller Adds 🧭

My earlier ESP32-4848S040 panel covered the essentials: track title and artist, album art, an animated progress arc, playback buttons and fixed playlist slots, all restyled live from Home Assistant. This build keeps that screen and adds the parts that need real data out of Music Assistant.

The Music Assistant ESP32 media controller ships as:

  • 🧩 A custom integration (media_controller) installed through HACS
  • ⚙️ Config Flow and Options Flow instead of YAML editing
  • 📦 A remote ESPHome package — your device YAML is ~40 lines, not 2,329
  • 📃 A live queue page and a real playlist list pulled from your Music Assistant library
  • 💡 Four optional room controls — Light 1, Light 2, Fan and AC

Hardware Stays Exactly the Same 🛠️

The Music Assistant ESP32 media controller runs on the ESP32-S3-4848S040: ESP32-S3, 16 MB flash, octal PSRAM at 80 MHz, an ST7701S 480×480 RGB panel and a GT911 capacitive touchscreen.

⚠️ Treat the display block as hardware-critical. The ST7701S init sequence, RGB/SPI/I²C pin maps, sync porches and the 12 MHz pixel clock are the values that actually work on this panel. Changing a porch or the clock is the fastest way to get a rolling or blank screen. If you want the deep dive on why those numbers matter, I covered the display bus in the original LVGL dashboard build.


Architecture: One Integration, One Package 🧩

The Music Assistant ESP32 media controller integration does not open its own Music Assistant connection. It resolves the config entry from the entity registry and reuses the client the official integration already owns:

registry_entry = er.async_get(hass).async_get(player_entity_id)
if registry_entry.platform != "music_assistant":
    raise MusicAssistantUnavailable(...)
config_entry = hass.config_entries.async_get_entry(
    registry_entry.config_entry_id)

One connection, one credential, zero hardcoded UUIDs. That single decision is what makes this Music Assistant ESP32 media controller installable by someone who is not me. 🙂

The Bounded Queue Window 📦

An ESP32 cannot hold a 500-track queue. It never gets one. The Music Assistant ESP32 media controller requests a fixed window around the current item:

DEFAULT_QUEUE_WINDOW_BEFORE = 5
DEFAULT_QUEUE_WINDOW_SIZE = 50
QUEUE_REFRESH_DELAY = 3.0
PLAYLIST_REFRESH_INTERVAL = timedelta(hours=6)

A queue refresh fires only when media_title actually changes, then waits three seconds. Skip five tracks quickly and four of those waits get cancelled — a generation counter plus an asyncio.Lock guarantee no overlapping requests and no stale payload overwriting a newer one.

Proxy Entities for Room Controls 💡

The switches page of the Music Assistant ESP32 media controller handles Light 1, Light 2, Fan and AC. Rather than baking your entity IDs into firmware, the integration creates four proxy entities that mirror whatever you picked in the UI. Remap a light in Options Flow and the panel follows — no reflash. An unconfigured or unavailable proxy simply reports unavailable while everything else keeps working. If you run grouped lights, my notes on syncing two Matter lights pair well with this page.


Wake Touch That Doesn’t Press a Button ✋

When the screen sleeps, LVGL is paused — not just dimmed. The wake touch is consumed:

on_touch:
  - if:
      condition:
        lambda: return !id(backlight).remote_values.is_on();
      then:
        - light.turn_on: backlight
on_release:
  - if:
      condition:
        lvgl.is_paused:
      then:
        - lvgl.resume:

LVGL resumes on release, so the gesture that woke the panel can never click, long-press, swipe, skip a track or toggle your AC. Grabbing the panel in the dark stops being a gamble.


Prerequisites ✅

Before you flash anything, the Music Assistant ESP32 media controller expects four things to already be in place:

  • A current Home Assistant install with HACS
  • The official Music Assistant integration with at least one exposed media_player
  • ESPHome for validation, compilation and flashing
  • A dedicated Home Assistant long-lived access token for the REST transport — created in the next section

Secrets: What !secret Actually Means 🔑

The device YAML below is full of lines like ha_token: !secret media_controller_ha_token. That is not a placeholder you replace with a value — !secret is an ESPHome directive that says look this name up in secrets.yaml. The credential lives in that one file, and the config you paste, share or screenshot only ever contains the key name.

In the ESPHome Device Builder add-on, open the three-dot menu in the top-right corner and pick Secrets. On disk it is /config/esphome/secrets.yaml; from the CLI it sits next to your device YAML. The Music Assistant ESP32 media controller needs five entries there:

wifi_ssid: "YourNetwork"
wifi_password: "your-wifi-password"
media_controller_api_encryption_key: "base64-key-from-esphome"
media_controller_ota_password: "any-password-you-choose"
media_controller_ha_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6..."

⚠️ Keep using your existing global secrets.yaml. Do not create a second one just for the Music Assistant ESP32 media controller. If your Wi-Fi keys are already named something else, rename the !secret references in the device YAML — do not rename working keys that your other ESPHome devices depend on.

Where Each Value Comes From

  • Wi-Fi — your own network credentials, nothing special
  • API encryption key — ESPHome Device Builder generates one when you create the device; copy it straight into secrets.yaml
  • OTA password — you invent it; it is what protects wireless updates afterwards
  • Home Assistant token — a long-lived access token, created in Home Assistant itself

To create that token: click your user name at the bottom of the Home Assistant sidebar, open the Security tab, scroll to Long-lived access tokens and choose Create token. Name it something you will recognise later, like media-controller.

⚠️ The token is shown exactly once. Copy it into secrets.yaml immediately — close that dialog without copying and your only option is deleting the token and creating a new one. Make it dedicated: one token for this panel, so you can revoke it from the same screen without breaking anything else you built.

⚠️ Never commit secrets.yaml to Git.

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.