Adds a Reachy Mini to Home Assistant with zero YAML. Drops a "Reachy Mini" device into your HA instance, polls the robot's daemon every 30 s, and exposes:
- read-only sensors for active app, app transport, and voice direction (microphone-array DoA),
- binary sensors for awake state, remote-session activity, and speech detection,
- a camera with MJPEG live view and JPEG stills, consuming the robot's WebRTC stream server-side (works alongside apps and mobile/desktop remote sessions; no SDK changes),
- writable selects for motor mode (enabled / disabled / gravity compensation), emotion, and dance,
- writable number sliders for speaker and microphone volume,
- one-shot action buttons for wake up, go to sleep, stop/restart the running app, play a test sound, restart the daemon, play emotion, and play dance.
Auto-discovery uses the _reachy-mini._tcp.local. mDNS advertisement
the daemon ships out of the box. Polling fans out to several of the
daemon's existing REST routes (/api/daemon/status,
/api/daemon/robot-app-lock-status, /api/state/doa,
/api/volume/current, /api/volume/microphone/current); writes POST
to /api/motors/set_mode/{mode}, /api/move/play/{wake_up,goto_sleep},
/api/apps/{stop,restart}-current-app, /api/volume/set, etc. No
extra protocols, no broker, no auth.
Until this repo is added to the default HACS registry, install it as a Custom Repository.
- Open HACS → Integrations → ⋮ → Custom repositories.
- Add
https://github.com/pollen-robotics/reachy_mini_homeassistantwith category Integration, then click Add. - Find Reachy Mini in the HACS list and click Download.
- Restart Home Assistant. The first restart takes a little longer:
HA installs the camera's WebRTC dependency (
aiortc), fetched as a sha256-pinned wheel from this repo (seewheels/README.mdfor why), so the host needs internet access. A failed download shows up in the log as "Setup failed for custom integration 'reachy_mini': Requirements ... not found" — restart to retry.
- Copy the
custom_components/reachy_mini/folder into your HA config directory:<config>/custom_components/reachy_mini/. - Restart Home Assistant.
- Make sure your Reachy Mini daemon is running on the same LAN.
For auto-discovery you need a daemon that advertises
manufacturer=Pollen Roboticsin its mDNS TXT record — that's thefeat/homeassistantbranch or any release that includes it. For manual setup any current daemon will do (the integration probes/api/daemon/status). - Within ~30 s of HA starting, you'll see a "Discovered: Reachy Mini" card under Settings → Devices & Services. Click Add.
- Confirm — your Reachy Mini appears as a device with all entities
grouped underneath, identified by its stable
unit_id.
No DNS / no IPs to type. If .local resolution is broken on your
network, you can still Manual configuration with the daemon's IP.
The integration creates one device per Reachy Mini, identified by
its stable unit_id (a hash of the audio device serial). Underneath:
| Entity | Unit | Notes |
|---|---|---|
| Active app | — | Local Python app or WebRTC peer name |
| Active app transport | — | local or webrtc |
| Voice direction | rad | 0 = left, π/2 = front, π = right |
| Entity | Device class | Notes |
|---|---|---|
| Awake | power |
True when motors are enabled or in gravity comp |
| Remote session | connectivity |
True when a remote client (mobile/desktop app over WebRTC) holds the robot app slot |
| Speech detected | sound |
Mic-array VAD signal (LAN-side speech-activity VAD) |
| Entity | Notes |
|---|---|
| Camera | MJPEG live view (~10 fps) + stills. HA consumes the robot's WebRTC camera producer (ws://<robot>:8443) through one shared session; available only while the robot is awake. |
| Entity | Options | Notes |
|---|---|---|
| Motor mode | enabled / disabled / gravity_compensation |
Picking an option POSTs to /api/motors/set_mode/{mode}. Use disabled as a soft E-stop (motors release torque). |
| Emotion | One entry per move in the emotions library | Stores the chosen emotion name; does not play it. Use the Play emotion button (or the reachy_mini.play_recorded_move service) to trigger playback. |
| Dance | One entry per move in the dances library | Stores the chosen dance name; does not play it. Use the Play dance button (or the reachy_mini.play_recorded_move service) to trigger playback. |
Drag the slider in the HA UI to change the value on the robot. The daemon may clamp/round to its supported range; the slider snaps to the value actually applied.
| Entity | Unit | Notes |
|---|---|---|
| Speaker volume | % | 0-100. The daemon plays a short confirmation sound after each change — existing SDK behaviour. |
| Microphone volume | % | 0-100. |
| Entity | Action | Notes |
|---|---|---|
| Wake up | POST /api/move/play/wake_up |
Goes through the wake-up move; the "Awake" binary sensor follows. |
| Go to sleep | POST /api/move/play/goto_sleep |
Plays the sleep move and releases torque. |
| Stop current app | POST /api/apps/stop-current-app |
Kills the currently running managed app (no-op when free). |
| Restart current app | POST /api/apps/restart-current-app |
Stop + start the current app — handy if it got stuck. |
| Play test sound | POST /api/volume/test-sound |
Plays impatient1.wav — quick speaker check. |
| Restart daemon | POST /api/daemon/restart |
Soft restart of the daemon process. Use sparingly. |
| Play emotion | POST /api/move/play/recorded-move-dataset/{dataset}/{move} |
Plays the emotion currently selected in the Emotion select entity. |
| Play dance | POST /api/move/play/recorded-move-dataset/{dataset}/{move} |
Plays the dance currently selected in the Dance select entity. |
The integration picks up the daemon's two preloaded move libraries
(pollen-robotics/reachy-mini-emotions-library and
pollen-robotics/reachy-mini-dances-library) at setup. For each
populated library you get:
- a select entity (
Emotion/Dance) listing every move in the library — picking does not play; it just stores the choice; - a button entity (
Play emotion/Play dance) that plays whatever is selected.
For automations and blueprints there's also a global service action,
reachy_mini.play_recorded_move, which takes a target device, a
dataset (HF repo path) and a move (string). The dataset can be one
of the bundled libraries or any custom HF dataset the daemon has
cached — the daemon validates unknown datasets.
A snapshot of the moves the SDK ships in its two bundled libraries. The
left column is what you see in the Emotion / Dance dropdowns; the
right column is the SDK move name you pass to the
reachy_mini.play_recorded_move service action. Future SDK releases
may add or rename moves — anything the daemon returns but isn't listed
here will appear in the dropdown using its raw SDK name.
Emoji-and-label mapping adapted from reachy-mini-desktop-app (Apache 2.0 © Pollen Robotics).
Emotions (81 moves)
| Display label | SDK move name |
|---|---|
| 😨 Fear | fear1 |
| 😩 Exhausted | exhausted1 |
| 🥰 Loving | loving1 |
| 🪩 Dance 3 | dance3 |
| 😑 Boredom 2 | boredom2 |
| 😌 Relief 1 | relief1 |
| 😟 Anxiety | anxiety1 |
| 🤢 Disgusted | disgusted1 |
| 👋 Welcoming 1 | welcoming1 |
| ⏳ Impatient 1 | impatient1 |
| 😭 Sad 1 | sad1 |
| 🤝 Helpful 2 | helpful2 |
| 😞 Resigned | resigned1 |
| 🤩 Amazed | amazed1 |
| 💭 Thoughtful 2 | thoughtful2 |
| 😵💫 Lost | lost1 |
| 😲 Surprised 1 | surprised1 |
| 🧘 Serenity | serenity1 |
| 😒 Displeased 1 | displeased1 |
| 🤷 Incomprehensible | incomprehensible2 |
| 😤 Irritated 2 | irritated2 |
| 🥹 Yes sad | yes_sad1 |
| 🕺 Dance 2 | dance2 |
| 💡 Understanding 1 | understanding1 |
| 🙄 Contempt | contempt1 |
| ❓ Inquiring 1 | inquiring1 |
| 😡 Rage | rage1 |
| 🦉 Attentive 2 | attentive2 |
| 👎 No | no1 |
| 🫣 Oops 1 | oops1 |
| 💪 Proud 3 | proud3 |
| 🚫 Reprimand 3 | reprimand3 |
| 😡 Reprimand 2 | reprimand2 |
| 😱 Scared | scared1 |
| 🙅♂️ No excited | no_excited1 |
| 🫴 Come | come1 |
| 🏆 Proud 2 | proud2 |
| ✨ Success 1 | success1 |
| 🥳 Enthusiastic 2 | enthusiastic2 |
| 😂 Laughing 1 | laughing1 |
| 😵 Dying | dying1 |
| 🌟 Success 2 | success2 |
| 🎊 Enthusiastic 1 | enthusiastic1 |
| 🧐 Curious | curious1 |
| 🤣 Laughing 2 | laughing2 |
| 😴 Tired | tired1 |
| 😤 Reprimand 1 | reprimand1 |
| 😎 Proud 1 | proud1 |
| 🙏 Grateful | grateful1 |
| 😫 Frustrated | frustrated1 |
| ☮️ Calming | calming1 |
| 👂 Attentive 1 | attentive1 |
| 🤬 Furious | furious1 |
| 😅 Oops 2 | oops2 |
| 😠 Irritated 1 | irritated1 |
| 👍 Yes | yes1 |
| 😕 Confused | confused1 |
| 🤝 Understanding 2 | understanding2 |
| 💃 Dance 1 | dance1 |
| 😳 Shy | shy1 |
| 🔍 Inquiring 2 | inquiring2 |
| 🤨 Uncertain | uncertain1 |
| 🤔 Thoughtful 1 | thoughtful1 |
| 😯 Surprised 2 | surprised2 |
| 😑 Displeased 2 | displeased2 |
| 🙄 Impatient 2 | impatient2 |
| 🤗 Welcoming 2 | welcoming2 |
| 😐 Indifferent | indifferent1 |
| 😢 Sad 2 | sad2 |
| 🙋 Helpful 1 | helpful1 |
| 🥺 Lonely | lonely1 |
| 😊 Cheerful | cheerful1 |
| 🤨 Inquiring 3 | inquiring3 |
| 😔 Downcast | downcast1 |
| 💤 Sleep | sleep1 |
| 🥱 Boredom 1 | boredom1 |
| 😬 Uncomfortable | uncomfortable1 |
| 👉 Go away | go_away1 |
| ⚡ Electric | electric1 |
| 😮💨 Relief 2 | relief2 |
| 😥 No sad | no_sad1 |
Dances (34 moves)
| Display label | SDK move name |
|---|---|
| 🫨 Stumble and recover | stumble_and_recover |
| 🎭 Chin lead | chin_lead |
| 🔃 Head tilt roll | head_tilt_roll |
| 🕴️ Jackson square | jackson_square |
| 🎐 Pendulum swing | pendulum_swing |
| 👁️ Side glance flick | side_glance_flick |
| 🤖 Grid snap | grid_snap |
| 😌 Simple nod | simple_nod |
| 🌊 Side to side sway | side_to_side_sway |
| 🥁 Polyrhythm combo | polyrhythm_combo |
| 🌀 Interwoven spirals | interwoven_spirals |
| 😏 Uh huh tilt | uh_huh_tilt |
| 🐓 Chicken peck | chicken_peck |
| 🙌 Yeah nod | yeah_nod |
| 🤘 Headbanger combo | headbanger_combo |
| 🙈 Side peekaboo | side_peekaboo |
| 💫 Dizzy spin | dizzy_spin |
| ⚡ Neck recoil | neck_recoil |
| 🪩 Groovy sway and roll | groovy_sway_and_roll |
| 📐 Sharp side tilt | sharp_side_tilt |
| 💍 Beyonce single ladies | beyonce-single-ladies |
| 👹 Demon hunters | demon-hunters-1 |
| 🌴 Eagles hotel california | eagles-hotel-california |
| 🎤 Eminem lose yourself | eminem-lose-yourself |
| ✨ Feel the magic in the air | feel-the-magic-in-the-air |
| 🎆 Katy perry fireworks | katy-perry-fireworks |
| 🍅 Las ketchup | las-ketchup |
| 🧟 Michael jackson thriller | michael-jackson-thriller |
| 🖤 Paint it black | paint-it-black |
| 😀 Pharrell williams happy | pharrell-williams-happy |
| 👑 Queen we will rock you | queen-we-will-rock-you |
| 🎀 Spice girls | spice-girls |
| 🎻 The fratellis whistle for the choir | the-fratellis-whistle-for-the-choir |
| ⚔️ The white stripes seven nation army | the-white-stripes-seven-nation-army |
Ready-made automation blueprints ship in the
blueprints/automation/reachy_mini/ folder of this repo. Each pre-fills
its entity dropdowns with the names this integration creates — you
typically only have to pick the action targets. Click an import badge,
then Import and Create automation in HA.
HA doesn't show the "Discovered" card.
- Your daemon is running but on an older version that doesn't advertise
the
manufacturer=Pollen RoboticsTXT record. Upgrade to the SDK build that includes thefeat/homeassistantwork. - mDNS isn't traversing your network. Confirm with
avahi-browse -ar(Linux) ordns-sd -B _reachy-mini._tcp(macOS). - HA's zeroconf component is disabled. Re-enable via Settings → Integrations.
"Cannot connect" when adding manually.
curl http://<host>:8000/api/daemon/status — if that returns a JSON
blob with "type": "daemon_status" and a version field, the
integration should also work. If not, check the daemon logs.
Entities show unknown after add.
The daemon backend hasn't finished starting yet (motor configuration takes ~5–10 s on first boot). Wait one update interval (30 s).
The camera is unavailable / the live view ended.
The camera exists only while the robot is awake — the daemon stops its WebRTC producer during sleep. Wake the robot and the camera returns on the next poll (≤30 s). An MJPEG live view also ends when the robot goes to sleep mid-stream; just reopen it after waking.
Some entities are unavailable but others work.
By design — the integration fans out across several SDK endpoints in
parallel each tick, and one failing endpoint only takes down the
entities backed by it (e.g. /api/state/doa returning 404 because
audio is disabled leaves the speech / voice-direction entities
unavailable but everything else keeps polling normally).
The integration is a thin client over the daemon's existing REST
surface — no extra protocols, no schema parsing beyond the documented
fields each endpoint returns. The fan-out coordinator (~80 lines of
Python) calls five GET endpoints in parallel each poll and does the
HA-shaping (awake, active_app_transport, remote_session_active,
active_app) locally on top of the raw values. Writes POST to the
same routes the dashboard / SDK clients have always used.
That means upgrading the daemon to a newer release rarely requires upgrading the integration too — only renames or removals of the specific routes the integration calls would break it.
Apache 2.0 — same as the upstream Reachy Mini SDK.