Skip to content

MQTT Integration

Bob McGlobus edited this page Aug 13, 2026 · 5 revisions

MQTT Integration

MQTT is Murdock's recommended Home Assistant integration. It is token-free in both directions: recognition results go out via HA's MQTT auto-discovery, and HA pushes context (which satellite is speaking, which media players are playing, presence) back in on retained topics. The legacy REST path (long-lived token + input_text helpers) still exists for setups without a broker.

In the add-on, the Mosquitto broker is auto-wired via HA's service API — zero configuration. Standalone, enter the broker host/credentials under Settings → MQTT in the Web UI.

Entities you get (auto-discovery)

All grouped under one Murdock device:

  • sensor.murdock_current_speaker — recognised name, or unknown
  • sensor.murdock_speaker_confidence — calibrated probability in %
  • sensor.murdock_speaker_distance — raw cosine distance
  • sensor.murdock_nearest_speaker — closest enrolled voice
  • sensor.murdock_speaker_role — the speaker's configured role
  • sensor.murdock_speaker_emotion — (experimental, usually empty)
  • sensor.murdock_active_satellite — which satellite the result came from
  • sensor.murdock_whisper_score — (experimental) measured whisper score
  • binary_sensor.murdock_speaker_recognized — ON/OFF
  • binary_sensor.murdock_whispering — (experimental) ON while the last utterance was whispered, regardless of whether the speaker was known

Topic reference

Topic Direction Payload
homeassistant/<comp>/murdock/<id>/config Murdock → HA discovery (retained)
murdock/status Murdock → HA online / offline (LWT, retained)
murdock/sensor/<name>/state Murdock → HA recognition state (retained)
murdock/event/recognition Murdock → HA full JSON event
murdock/active_satellite HA → Murdock {"id": "...", "area": "..."}
murdock/context/media/<entity_id> HA → Murdock {"playing": true, "area": "..."} (retain!)
murdock/context/<room>/presence HA → Murdock {"present": true} (retain!)
murdock/context/<room>/tv HA → Murdock legacy single-TV topic (retain!)

Recognition event payload

murdock/event/recognition (MQTT) and the HA event speaker_recognition_detected (REST) carry the same JSON shape.

{
  "speaker": "Jonas",
  "confidence": 0.9432,
  "satellite_id": "wohnzimmer",
  "is_known": true,
  "timestamp": 1785000000.12,
  "distance": 0.31,
  "threshold": 0.38,
  "nearest_speaker": "Nils",
  "nearest_distance": 0.61,
  "margin": 0.30,
  "weight": 1.0,
  "role": "admin"
}
Field Meaning
speaker Name on a verified match, null otherwise
reason Only when speaker is null: unknown, uncertain, tv-noise, early-reject, short, no-speakers, embed-failed
nearest_speaker / nearest_distance On a match the runner-up; otherwise the closest candidate
margin nearest_distance − distance — input to the margin gate
weight 1.0 when verified, decaying linearly to 0 at threshold + 0.15 — how much a speaker-bound rule is trusted
uncertain true when the margin gate downgraded a match
emotion / emotion_confidence Only when emotion classification is enabled
whisper / whisper_score Whether the utterance was whispered, and the measured score (see Whisper detection)
ambiguities Sidecar hints: [{original, alternative, kind}], kind ∈ alternative / additional / reading (see Speaker Context)

Two properties worth relying on:

  • The event fires on every completed utterance, including non-recognitions. Before 0.8.0 a blocked or unknown utterance left the previous speaker standing until something else overwrote it.
  • speaker is null rather than a sentinel string. Automations written against "unknown" / "tv-noise" in speaker need to read reason instead (⚠️ changed in 0.8.0). The MQTT sensor states are unchanged — only the JSON event moved.

Inbound context topics must be published with retain: true so Murdock picks up the last state immediately after a (re)connect. The momentary active_satellite signal needs no retain.

Satellite identification

HA's Assist pipeline does not tell the STT stage which device is speaking — the Wyoming Transcribe.name field arrives empty. Murdock recovers the identity from MQTT: a small automation publishes the active satellite when it starts listening. Murdock uses it only when fresh (≤ 30 s) and when no name came in over Wyoming, so directly-connected wyoming-satellite devices with their own name still win.

alias: Murdock — publish active satellite
trigger:
  - platform: state
    entity_id:
      - assist_satellite.living_room    # ← your assist_satellite entities
      - assist_satellite.kitchen
    to: listening
action:
  - service: mqtt.publish
    data:
      topic: murdock/active_satellite
      payload: >-
        {{ {'id': trigger.entity_id, 'area': area_name(trigger.entity_id)} | to_json }}
mode: queued
max: 10

Publishing the id (entity id) lets two satellites in the same room be tuned separately; the area feeds room-based media gating below.

Media context — one automation for everything

Each TV/radio/speaker publishes its own playing-state and room; Murdock tightens the verify threshold while something plays in the active satellite's room (see Tuning for the per-source matrix).

alias: Murdock — publish media players
trigger:
  - platform: state
    entity_id:
      - media_player.living_room_tv     # ← every TV/radio you care about
      - media_player.kitchen_radio
action:
  - service: mqtt.publish
    data:
      topic: "murdock/context/media/{{ trigger.entity_id }}"
      retain: true
      payload: >-
        {{ {'playing': is_state(trigger.entity_id,'playing'),
            'area': area_name(trigger.entity_id)} | to_json }}
mode: queued
max: 20

Both automations are generated with your topic prefix in the Web UI's MQTT card.

Ducking media while a satellite listens

Murdock deliberately does not do this. It only ever sees audio after the wake word has already fired, so ducking from here would ramp the volume down during your first word — too late to help recognition, and loudest exactly when it is most in the way.

Home Assistant's own assist_satellite entity flips to listening the moment the wake word lands, which is the right trigger. A blueprint ships with the repo:

Import: Murdock — duck media while Assist is listening

Pick the satellite, pick the media players in that room, choose how far to duck. One automation per room.

Three properties worth knowing:

  • Only the volume is touched. Playback state is left alone on purpose — restoring a snapshot could resume something you paused during the conversation.
  • A safety timeout restores the volume even if the satellite never reports idle, so a crashed pipeline cannot leave a room quiet forever.
  • mode: restart, so a second wake word while the assistant is still answering resets the timer instead of queueing a second restore.

The volume comes back when the satellite returns to idle, which is after the spoken answer — so the room stays quiet while the assistant talks, not just while it listens.

Debugging

GET /api/settings/mqtt-context (or via the add-on ingress) shows the live context cache: connected state, every media entry with age, plus active_satellite / active_satellite_area. If an automation doesn't arrive, check there first.

Clone this wiki locally