I’ve got a RPi3b+ with a hifiberry amp that I use to directly play music from the raspberry pi. For years, from before I used HA, I’ve been tinkering with setups to make Spotify (Connect) work trouble free. The main trouble is always to start playback without the native Spotify app. I recently got some ideas that I think are worth sharing. Maybe it gives some inspiration to others.
For a long time I’ve used variants of librespot to allow a Spotify Connect. The latest variant I’ve been using is go-librespot. The neat thing about this one, is that it has an API, which also proxies the Spotify Web API and has websocket events that you can listen to.
These three things gave me two ideas:
- Use the API to start Spotify playback (without the Spotify app)
- Use the events to speed up updates of the HA Spotify player, and no longer have a delay in HA.
The first one was pretty easy. Important: It does require you have a single account connected to go-librespot using the stored or interactive credentials. I don’t think it works with zeroconf authentication, at least not without still authenticating using the Spotify app first.
You can then use something like this to start playback on go-librespot:
rest_command:
librespot_load:
url: "http://host.docker.internal:24879/player/load"
payload: "uri={{uri}}&play=true"
method: POST
librespot_get_latest_episode:
url: "http://host.docker.internal:24879/web-api/v1/shows/{{ show }}/episodes?limit=1"
method: GET
The librespot_load
service then starts playback if you supply it a spotify URI. I’ve also added an example of librespot_get_latest_episode
how I use the proxied Spotify Web API to fetch the latest episode of a podcast.
The second idea to use the go-librespot events to instantly update the HA Spotify entity was a bit more involved. First you need to connect to the websocket, and I’m then transfering those events to my MQTT server (I guess you could use the HA REST API as well). I wrote this Python script for it:
import asyncio
import websockets
import paho.mqtt.client as mqtt
import signal
# Configuration
WEBSOCKET_URL = "ws://127.0.0.1:3678/events"
MQTT_BROKER = "127.0.0.1"
MQTT_USERNAME = ""
MQTT_PASSWORD = ""
MQTT_PORT = 1883
MQTT_TOPIC = "librespot"
# Initialize MQTT client
mqtt_client = mqtt.Client()
mqtt_client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
mqtt_client.connect(MQTT_BROKER, MQTT_PORT, 60)
mqtt_client.loop_start()
# Graceful shutdown event
shutdown_event = asyncio.Event()
def handle_sigint():
print("Shutdown requested. Cleaning up...")
shutdown_event.set()
async def websocket_to_mqtt():
async with websockets.connect(WEBSOCKET_URL, ping_interval=30, ping_timeout=2) as websocket:
print(f"Connected to WebSocket: {WEBSOCKET_URL}")
while not shutdown_event.is_set():
try:
message = await asyncio.wait_for(websocket.recv(), timeout=5.0)
print(f"Received WebSocket message: {message}")
mqtt_client.publish(MQTT_TOPIC, message)
print(f"Published to MQTT topic {MQTT_TOPIC}")
except asyncio.TimeoutError:
continue
except websockets.ConnectionClosed:
print("WebSocket connection closed. Reconnecting...")
break
except Exception as e:
print(f"Error: {e}")
async def main():
loop = asyncio.get_running_loop()
loop.add_signal_handler(signal.SIGINT, handle_sigint)
loop.add_signal_handler(signal.SIGTERM, handle_sigint)
while not shutdown_event.is_set():
try:
await websocket_to_mqtt()
except Exception as e:
print(f"Connection failed: {e}")
await asyncio.sleep(5)
mqtt_client.loop_stop()
mqtt_client.disconnect()
print("Shutdown complete.")
if __name__ == "__main__":
asyncio.run(main())
I’ve then got a automation triggered by MQTT that updates the Spotify entity whenever the go-librespot event has as type playing
.
alias: spotify/update
description: ""
triggers:
- trigger: mqtt
topic: librespot
conditions:
- condition: template
value_template: "{{ trigger.payload_json[\"type\"] == \"playing\" }}"
actions:
- action: homeassistant.update_entity
metadata: {}
data:
entity_id:
- media_player.spotify_kiosk
mode: queued
max: 10
Maybe my situation is somewhat unique, and most people would use Music Assistant instead. But I can’t, as MA doesn’t run on RPi3b+. Hopefully this give some inspiration for some. Trying to work around the woes of Spotify remains a challenge, but I think this is the best setup I’ve reached so far,