Some hardware links on this page are affiliate links. As an Amazon Associate I earn from qualifying purchases. We may earn a commission if you buy through them, at no extra cost to you. See the affiliate disclosure.
A speaker that is playing should not make you open a phone to see the track. This firmware puts that on the 320×240 panel of an ESP32-2432S028R, the usual one-USB Cheap Yellow Display. While the media player you name is playing, the screen shows album art, title, artist, a progress bar, and two touch targets. After it has been idle, the same panel is a clock with the room temperature and a weather line.
The same board, used as a wall of buttons or as a thermostat, is CYD touch control panel, CYD touch thermostat, and CYD desk companion. This one only watches a media player.
ha_base_url in the file is https://home.example.com. Replace it with the URL Home Assistant uses for /api/media_player_proxy/... pictures. Do not publish that URL, and do not publish the long-lived token.
What problem it solves
You want the track that is playing, on the desk, from whatever media player Home Assistant already has, and a clock when nothing is playing.
How it works
ESPHome draws two LVGL pages. Home Assistant is the source of the player, the temperature, and the weather. The display does not talk to a speaker’s own web page.
- Player — a substitution names one
media_player. When its state becomesplaying, the backlight goes to full and the playing page is shown. Any other state starts an idle timer (pack default 45 seconds). When that timer finishes, the backlight drops to 40% and the clock page is shown. - Art —
entity_pictureis loaded as a 120×120 JPEG. A path that already starts withhttpis used as-is. A relative path is prefixed withha_base_url. The request sends the token fromsecrets.yamlasAuthorization. TLS verification is off so a local certificate still loads. A missing picture stays on the placeholder URL and does not block the title. - Text and progress — title and artist update when those attributes change. Every 5 seconds the progress bar is
media_position / media_duration. A player with no duration leaves the bar at zero. - Touch — Play calls
media_player.media_play_pause. Next callsmedia_player.media_next_track. Both go through the ESPHome device connection, so the device has to be allowed to perform Home Assistant actions. There is no previous button. - Clock page — SNTP time as HH:MM, the temperature substitution, and the weather entity’s state plus its temperature attribute. The clock page is the one shown at boot.
Wi-Fi, the API key, OTA, and the token stay in secrets.yaml.
Hardware notes
- Board. Stock one-USB resistive CYD. Display is MIPI SPI, model ILI9341, color order BGR. SPI clock GPIO14, MOSI GPIO13, MISO GPIO12, CS GPIO15, DC GPIO2. Backlight is LEDC on GPIO21. Touch is XPT2046 on a second SPI bus: clock GPIO25, MOSI GPIO32, MISO GPIO39, CS GPIO33, interrupt GPIO36. Rotation is 90, which matches the USB socket on the right for the usual case.
- Other CYDs. A two-USB board or an ST7789 panel needs a different display model and pin map. This file does not cover those. If touch is mirrored, flip
touch_mirror_xortouch_mirror_yand reflash. - Power. USB is enough. The panel is not a battery device. Later updates can be OTA.
- Player. Any
media_playerthat exposesentity_picture,media_title, andmedia_artist. A player that omits art still shows the text. Play and Next only work if that player implements those actions. - Token. Create a long-lived access token under your profile in Home Assistant. The secrets example expects the value to start with
Bearer, including the space. Store it only insecrets.yaml.
Snippet
Names and the base URL are substitutions. The pages and the touch actions are in the pack.
substitutions:
device_name: now-playing
media_player: media_player.change_me
temperature_entity: sensor.change_me_temperature
weather_entity: weather.change_me
ha_base_url: "https://home.example.com"
idle_seconds: "45"
Setup outline
- Copy the secrets example into your ESPHome secrets. Fill Wi-Fi, a new API key, OTA, the fallback password, and
ha_access_token. - Paste the device YAML. Set the media player, the temperature sensor, the weather entity, and
ha_base_url. - Validate, then flash over USB. Accept the device in Home Assistant. Open the ESPHome device and enable Allow the device to perform Home Assistant actions.
- Start playback. The playing page should show title and artist within a few seconds, and art if
entity_pictureis a JPEG the panel can fetch. - Pause and wait out the idle time. The clock, room line, and weather line should replace the track. Play again and the track page should return immediately.
- Tap Play and Next. The player should pause or skip. If the taps do nothing, the allow-actions checkbox is still off.
Troubleshooting
- The clock never leaves
--:--. SNTP has not synced. Check Wi-Fi. The timezone in the file isUTC. Change both time blocks if you want a local clock. - Title updates, art does not. The picture URL is wrong, or it is not a JPEG. Relative pictures need
ha_base_urlwith no trailing path, for examplehttps://home.example.com. The token must beBearerplus the token. A 401 in the ESPHome log is that header. - Art works on the LAN and fails on a picture host.
verify_ssl: falseis for a local Home Assistant certificate. A public host that is not JPEG, or a host that blocks the token header, still fails. The title does not depend on art. - Touch is offset. Recalibrate
touch_x_min,touch_x_max,touch_y_min, andtouch_y_max. Start from the pack values. Mirroring is the other pair of substitutions. - Play does nothing. The device is not allowed to call Home Assistant services, or this player has no play/pause action. The log line is on the ESPHome device, not on the media player.
- The screen stays on the track after pause. The idle delay restarts whenever the player state changes. Wait the full
idle_secondsafter the state settles onpausedoridle. - A two-USB CYD stays white. This YAML is the ILI9341 one-USB map. Do not keep flashing it on a different panel.
Credits
Based on esp32-wiim-now-playing-display. That project draws now-playing on a Guition ESP32-S3 480×480 and talks to a WiiM player directly. This pack is the Cheap Yellow Display, and the player is any Home Assistant media_player, with art taken from entity_picture. It does not speak a speaker’s private API.