Wiki

Random Audiobook on MPD

from kekskurse at 2025-07-03 20:43:01 +0200 +0200

I want the Zigbee light switch attached directly to the headboard of my bed to play a random Sherlock Holmes or Three Investigators episode when pressed and held. To do this, I first need to write a Python script that connects to my MPD and retrieves a list of all possible audiobooks, then selects one at random and plays it. The script then needs to be made available on the Pi running Home Assistant and linked to the button.

Setup Homeassistant

I run Homeassistant inside a Docker container. Therefor i need to mount the folder with the python script inside the container. I use the /opt directly in this setup

services:
  homeassistant:
    image: "ghcr.io/home-assistant/home-assistant:stable"
    volumes:
      - /opt/:/opt/root
...

after that i could make the script visible to homeassistant by adding the following content to my configuration.yaml:

shell_command:
  hoerbuch: python3 /opt/root/randomhoerbuch

Than i used the Homeassistant GUI to create an automation which run the script on the hold action of the button:

alias: Button Bed Long
description: ""
trigger:
  - platform: device
    domain: mqtt
    device_id: cdb815a53346bc4bca446b097a2da234
    type: action
    subtype: hold
    discovery_id: 0x00158d00027d83ba action_hold
condition: []
action:
  - service: shell_command.hoerbuch
    data: {}
mode: single

Python code

#!/bin/python3
import random
from mpd import MPDClient

# Konfigurierbare Einstellungen
MPD_HOST = "192.168.1.101"
MPD_PORT = 6600
MPD_PASSWORD = None  # oder z.B. "meinPasswort"
ALBUM_ARTISTS = ["Sherlock Holms - Aus den Tagebüchern"]  # Beispielkünstler

def connect_to_mpd():
    client = MPDClient()
    client.timeout = 10
    client.idletimeout = None
    client.connect(MPD_HOST, MPD_PORT)
    if MPD_PASSWORD:
        client.password(MPD_PASSWORD)
    return client

def get_albums_by_artist(client, artist):
    albums = client.list("album", "albumartist", artist)
    return [album for album in albums if album]

def get_songs_from_album(client, artist, album):
    # Versuche zuerst mit albumartist
    songs = client.find("albumartist", artist, "album", album)
    if not songs:
        # Fallback auf artist, falls albumartist nicht funktioniert
        songs = client.find("artist", artist, "album", album)
    print(f"Keine Songs gefunden für:\nArtist: {artist}\nAlbum: {album}")
    return songs

def main():
    client = connect_to_mpd()


    # Alle Alben sammeln
    artist_album_map = {}
    for artist in ALBUM_ARTISTS:
        albums = get_albums_by_artist(client, artist)
        if albums:
            artist_album_map[artist] = albums

    # Zufällig Künstler und Album wählen
    if not artist_album_map:
        print("Keine Alben gefunden.")
        return

    artist = random.choice(list(artist_album_map.keys()))
    album_entry = random.choice(artist_album_map[artist])
    album = album_entry["album"] if isinstance(album_entry, dict) else album_entry

    print(f"Spiele zufälliges Album:\nKünstler: {artist}\nAlbum: {album}")

    # Songs holen und abspielen
    songs = get_songs_from_album(client, artist, album)

    client.clear()
    for song in songs:
        client.add(song["file"])
    client.play()

    client.close()
    client.disconnect()

if __name__ == "__main__":
    main()