---
title: "Identify the music playing in a room with Home Assistant"
description: "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."
slug: "/resources/integrations/home-assistant"
section: "integrations"
keywords: [audd, home assistant, music assistant, song recognition, smart home, sensor]
---

# Identify the music playing in a room with Home Assistant

If you run [Home Assistant](https://www.home-assistant.io/) — on its own or
with the [Music Assistant](https://music-assistant.io/) 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](https://docs.audd.io/enterprise) and
[streams](https://docs.audd.io/streams) endpoints.)

## Prerequisites

- An API token from [dashboard.audd.io](https://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:

```python
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):

```bash
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:

```bash
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:

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

Then recognize the captured file:

```python
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:

```yaml
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](https://www.home-assistant.io/integrations/shell_command/)
and call it from a script, a dashboard button, or a voice assistant:

```yaml
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:

```json
{
  "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"
  }
}
```

| Field | Meaning for the integration |
|---|---|
| `artist`, `title` | What's playing — the sensor state. |
| `album`, `release_date`, `label` | Extra tags for a richer card or logbook entry. |
| `timecode` | Position within the *matched track* at the clip point — not the clip's offset. |
| `song_link` | A 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](/resources/recipes/now-playing-widget).

---

**Related**

- [Build a now-playing widget for a livestream](/resources/recipes/now-playing-widget)
- [AudD Music Assistant plugin (draft)](https://github.com/AudDMusic/audd-music-assistant-plugin)
- [Python SDK docs](https://docs.audd.io/sdks/python)
- [API reference](https://docs.audd.io)