Integration

Identify the music playing in a room with Home Assistant

Recognize the song playing in a room from Home Assistant or Music Assistant — capture a short audio clip on the server, send it to AudD, and expose artist and title as a sensor and a service you can automate on.

view .md auddhome assistantmusic assistantsong recognition

If you run Home Assistant — on its own or with the Music Assistant add-on — you can ask it what song is playing in a room and act on the answer. This page wires AudD into Home Assistant: the server captures a few seconds of audio from a stream or a microphone, sends it to AudD for recognition, and exposes the result as a sensor and a service. From there you can announce the track on a speaker, log it, or trigger any automation.

AudD maintains a draft Music Assistant plugin you can use as a starting point: https://github.com/AudDMusic/audd-music-assistant-plugin.

The plugin is a proof-of-concept, not a finished add-on. Treat it as a reference implementation to read and adapt rather than a maintained, install-and-forget integration. The standalone approach below uses only the AudD API and Home Assistant’s built-in command-line and REST helpers, so it works regardless of the plugin’s state.

What you’ll build

Three parts, all running on the Home Assistant host:

  1. An audio capture step. A few seconds of audio from the room, grabbed on the server — ffmpeg pulling a snippet from a media stream (an Icecast URL, an HLS feed, a Sonos/AirPlay loopback source) or from a microphone device. AudD’s standard endpoint takes a short audio clip and responds in under 2 seconds, so a 5–10 second clip under the 10 MB file-size cap is all you need.
  2. An AudD recognition call. Send the clip to POST https://api.audd.io/ with your token and read back the artist and title.
  3. A Home Assistant surface. A command_line sensor that holds the last recognized track, and/or a script/service you call on demand from an automation, a dashboard button, or a voice command.

The standard endpoint is the right one here: you’re identifying one short clip of what’s playing now, not transcribing a whole file. (For full-length recordings or continuous streams, see the enterprise and streams endpoints.)

Prerequisites

  • An API token from dashboard.audd.io. The public test token works on the standard endpoint (10 requests/day) if you just want to confirm the flow before signing up.
  • Home Assistant, with the ability to add command_line entities to your configuration (Supervised, Container, or Core installs all support this).
  • ffmpeg available on the host — Home Assistant OS already bundles it; on other installs, install it.
  • An audio source you can reach from the host: a stream URL, or a capture device. For a copy-paste smoke test you can use the known sample file https://audd.tech/example.mp3.

Walkthrough

Step 1: Recognize one clip

Start with the smallest possible call: send an audio clip to the standard endpoint and read the result. Here it is in Python with the official SDK (pip install audd), using the sample file so it runs as-is:

from audd import AudD

audd = AudD("your-api-token")  # or "test" for 10 free requests/day

result = audd.recognize("https://audd.tech/example.mp3")

if result is None:
    print("no match")
else:
    print(f"{result.artist} — {result.title}")

recognize returns None on no match — a clean “nothing recognized,” not an error. When it matches you get back artist, title, album, release_date, label, timecode, and song_link.

If you’d rather not add a Python dependency on the host, the same call is a plain HTTP POST — handy for a command_line sensor (Step 3):

curl -s https://api.audd.io/ \
  -F api_token=your-api-token \
  -F url=https://audd.tech/example.mp3

Step 2: Capture a clip from the room

Replace the sample file with real audio captured on the server. AudD accepts a local file path, raw bytes, or a URL, so capture a short clip with ffmpeg and hand the file to AudD.

From a media stream (Icecast/HLS/Sonos URL) — grab 8 seconds:

ffmpeg -y -i "http://your-stream.local:8000/living-room.mp3" \
  -t 8 -ac 1 -ar 44100 /tmp/clip.mp3

From a capture device (a USB mic, or a loopback of what a speaker is playing) — use the appropriate ffmpeg input for your platform, e.g. ALSA:

ffmpeg -y -f alsa -i default -t 8 -ac 1 -ar 44100 /tmp/clip.mp3

Then recognize the captured file:

from audd import AudD

audd = AudD("your-api-token")
result = audd.recognize("/tmp/clip.mp3")
print(None if result is None else f"{result.artist} — {result.title}")

A short mono clip keeps you well under the 10 MB cap and recognizes just as well as a longer one — AudD only needs a few seconds of clean audio.

Capturing what a speaker is playing requires a loopback source. A microphone in the room picks up room noise; for clean recognition, capture the stream URL the player is pulling, or a software loopback (e.g. an ALSA snd-aloop device or a PulseAudio monitor) of the output. The room mic still works for “what’s that song on the radio” use cases.

Step 3: Expose it to Home Assistant

Add a command_line sensor that captures a clip and recognizes it, exposing the track as the sensor state. Put this in your configuration.yaml (or a command_line: package). The command captures with ffmpeg, posts to AudD, and prints Artist — Title for the sensor to read:

command_line:
  - sensor:
      name: Living Room Now Playing
      unique_id: living_room_now_playing
      command: >-
        ffmpeg -y -i "http://your-stream.local:8000/living-room.mp3"
          -t 8 -ac 1 -ar 44100 /tmp/clip.mp3 >/dev/null 2>&1 &&
        curl -s https://api.audd.io/
          -F api_token=your-api-token
          -F file=@/tmp/clip.mp3
        | python3 -c 'import sys,json; r=json.load(sys.stdin).get("result");
          print(f"{r[\"artist\"]} - {r[\"title\"]}" if r else "Nothing playing")'
      scan_interval: 300
      command_timeout: 30

This sensor re-checks every 5 minutes. Note -F file=@/tmp/clip.mp3 uploads the captured bytes; for a stream you can recognize without ffmpeg by sending -F url=... directly. AudD returns result: null (here rendered as “Nothing playing”) when nothing matches — distinct from an error.

To recognize on demand rather than on a timer, wrap the same command in a shell command and call it from a script, a dashboard button, or a voice assistant:

shell_command:
  identify_living_room: >-
    ffmpeg -y -i "http://your-stream.local:8000/living-room.mp3"
      -t 8 -ac 1 -ar 44100 /tmp/clip.mp3 &&
    curl -s https://api.audd.io/ -F api_token=your-api-token -F file=@/tmp/clip.mp3
      -o /config/www/last_recognition.json

An automation can then call shell_command.identify_living_room, read the artist and title from the JSON, and announce it on a speaker.

Step 4: Automate on the result

Once the track is in a sensor or a JSON file, it’s ordinary Home Assistant state. A few things you can wire up:

  • Announce the song on a TTS-capable speaker when the sensor changes.
  • Log to a logbook or history graph so you have a played-tracks timeline for a room.
  • Trigger scenes — dim the lights when a particular artist comes on.
  • Expose a “What’s playing?” voice command through Assist that calls the shell command and speaks the result.

The Music Assistant plugin

If you run Music Assistant, AudD’s draft plugin does the capture-and-recognize loop inside Music Assistant itself rather than via command_line: https://github.com/AudDMusic/audd-music-assistant-plugin.

It’s a useful reference for two things the standalone approach hand-rolls: deciding which source to capture (Music Assistant already knows the active player and its stream) and surfacing the result as a Music Assistant entity rather than a generic sensor. Because it’s a proof-of-concept, read it as a worked example — the AudD call inside it is the same POST https://api.audd.io/ with api_token and the captured audio shown above.

What you get back

A successful recognition from the standard endpoint:

{
  "status": "success",
  "result": {
    "artist": "Imagine Dragons",
    "title": "Warriors",
    "album": "Smoke + Mirrors (Deluxe)",
    "release_date": "2015-02-17",
    "label": "KIDinaKORNER/Interscope Records",
    "timecode": "00:31",
    "song_link": "https://lis.tn/warriors"
  }
}
FieldMeaning for the integration
artist, titleWhat’s playing — the sensor state.
album, release_date, labelExtra tags for a richer card or logbook entry.
timecodePosition within the matched track at the clip point — not the clip’s offset.
song_linkA universal lis.tn link; append ?thumb for cover art.

A no-match comes back as "result": null with "status": "success": nothing was recognized, which your sensor should render as a normal idle state, not an error. Any field AudD returns that the SDK doesn’t surface as a typed property is available on the result’s extras map.

Handling errors

For a room-recognition integration the cases that matter:

  • Authentication errors — bad or missing token. The HTTP response carries status: "error" with an error_code; fail loudly in your script rather than silently showing “Nothing playing.”
  • Quota errors — you’ve hit your request limit (the test token is capped at 10/day). Back off the scan_interval rather than retrying hard.
  • No match (result: null) — not an error. Render an idle state.
  • Capture failures — ffmpeg couldn’t reach the source or produced an empty file. Guard the chain with && (as above) so AudD isn’t called with an empty clip, and check command_timeout is long enough for capture plus the under-2-second recognition.

With the Python SDK, the same cases surface as typed exceptions (AudDAuthenticationError, AudDQuotaError, AudDInvalidAudioError) you can catch around the recognize call.

Going further

  • Cover art on a dashboard card. Append ?thumb to song_link for the cover image and show it in a picture-entity card.
  • Per-room sensors. Duplicate the command_line sensor with a different capture source per room; each becomes its own entity.
  • Continuous radio instead of on-demand. If you want a room’s audio recognized continuously rather than polled, that’s the streams endpoint — see Build a now-playing widget for a livestream.

Related

Reading this as an AI agent? The raw Markdown is at integrations/home-assistant.md, and the full index is /resources/llms.txt.