Home Assistant Nabu puck에서 오디오가 나오지 않는 문제: assist_satellite.announce 사용하기
요약
Home Assistant Nabu puck에서 TTS 호출 시 상태는 'playing'으로 표시되지만 소리가 나지 않는 문제를 다룹니다. 원인은 해당 장치가 일반 미디어 플레이어가 아닌 assist_satellite 엔티티로 동작하기 때문이며, 올바른 서비스 호출 방식을 제안합니다.
핵심 포인트
- Nabu puck은 일반 미디어 플레이어가 아닌 assist_satellite 엔티티로 동작함
- tts.speak 호출 시 상태는 'playing'으로 변하지만 실제 오디오는 출력되지 않을 수 있음
- ESPHome 통합을 통해 장치가 AssistSatelliteEntity로 노출됨을 이해해야 함
- 문제 해결을 위해 assist_satellite.announce 서비스 사용 권장
Home Assistant Voice (Nabu) puck에 로컬 음성 파이프라인 (voice pipeline)을 연결하고, 자동화 (automation)에서 tts.speak를 실행했는데 아무 소리도 들리지 않는 경우가 있습니다. 파이프라인은 실행됩니다. Home Assistant는 TTS 호출이 성공했다고 보고합니다. 하지만 음성은 나오지 않습니다. 더 최악인 것은, 개발자 도구 (Developer Tools)에서 확인해 보면 media_player 엔티티가 예상대로 정확히 playing 상태로 전환된다는 점입니다. 즉, 작동하는 것처럼 보이기 때문에 아무런 소리도 들리지 않는 서비스 호출을 한 시간 동안 멍하니 바라보게 만듭니다.
근본 원인: 이 puck은 일반적인 미디어 플레이어 (media player)가 아닙니다. 이것은 assist_satellite 엔티티이며, 이로 인해 TTS를 라우팅 (route)하는 방식이 달라져야 합니다. 이 문제를 해결하고 나면 두 가지 후속 함정이 기다리고 있습니다. 하나는 사용 중인 새틀라이트 (satellite)에 공지 (announcing)를 시도하는 것이고, 다른 하나는 원래의 "음성 없음" 증상과 유사하게 나타나는, 조용히 충돌한 Piper 애드온 (add-on)입니다. 여기에서 문제가 발생한 상황부터 시도, 그리고 해결까지의 전 과정을 다룹니다.
문제 (Problem)
텍스트를 puck으로 전송해도 전혀 말하지 않습니다. 현재 이 문제를 겪고 있는 분들을 위한 검색어는 다음과 같습니다: Home Assistant Nabu voice no audio, Voice PE tts.speak no sound, assist_satellite no speech media_player.
가장 명백한 서비스 호출 — puck의 미디어 플레이어에 텍스트를 말하게 하는 것:
service: tts.speak
target:
entity_id: tts.piper # 사용 중인 TTS 엔진 엔티티
...
개발자 도구 (Developer Tools) → 상태 (States)에서, media_player.home_voice는 성실하게 playing으로 전환되며 미디어 메타데이터 (media metadata)를 보여줍니다. 하지만 스피커는 침묵을 유지합니다. 이것이 함정입니다: 상태 변화는 실제이지만, 오디오는 실제가 아닙니다.
진단 (Diagnosis)
이 퍽(puck)은 단순한 미디어 플레이어가 아닙니다. Home Assistant Voice puck은 ESPHome을 실행하며, ESPHome 통합(integration)은 이를 assist_satellite 엔티티로 노출합니다. 이는 "기저의 Assist 파이프라인(pipeline)을 따르는 상태를 가진 음성 새틀라이트(voice satellite)를 나타내는" 엔티티입니다. esphome 및 voip 통합 모두 AssistSatelliteEntity로 전환되었습니다. 이는 장치가 채택(adopted)되는 즉시 적용되는 사항이며, Whisper 애드온(add-on) 설치와는 무관합니다. (Whisper는 파이프라인에 로컬 STT(Speech-to-Text)를 제공하는 한 가지 방법일 뿐이며, Home Assistant Cloud가 나머지 두 가지 방법입니다. 이 중 어느 것도 새틀라이트 엔티티를 생성하는 주체는 아닙니다.) 만약 로컬 STT를 설정할 때쯤에야 퍽이 새틀라이트라는 사실을 깨달았다면, 그것은 우연일 뿐 인과관계가 아닙니다.
새틀라이트는 구성된 파이프라인에 의해 구동되는 자체 오디오 경로를 소유합니다. media_player_entity_id를 사용하는 tts.speak는 대신 일반적인 미디어 플레이어 인터페이스를 대상으로 합니다. 우리의 설정에서 해당 인터페이스는 새틀라이트에서 사실상 흔적만 남은 상태입니다. HA는 media_player 엔티티 상태를 playing으로 업데이트하지만, 음성을 새틀라이트의 공지(announcement) 경로로 라우팅하는 것이 아무것도 없기 때문에 오디오가 출력되지 않습니다. 오류도, 로그 라인도 없이 그저 침묵만이 흐릅니다. (새틀라이트에 tts.speak를 실행했을 때 침묵이 흐르는 것을 목격한 것은 우리뿐만이 아닙니다. 커뮤니티의 "여전히 tts.speak가 작동하지 않습니다" 스레드들이 이를 증명합니다. 다만 정확한 동작은 장치와 TTS 엔진에 따라 다르므로, 이를 문서화된 규약이 아닌 우리 설정에서의 관찰 결과로 취급하십시오.)
새틀라이트의 오디오 경로를 통해 실제로 라우팅되는 액션은 assist_satellite.announce이며, 이는 "새틀라이트에 구성된 텍스트 음성 변환(TTS) 시스템을 사용하여" 메시지를 미디어 ID로 변환합니다.
우리가 시도한 것 (그리고 실패한 이유)
막다른 길 1: media_player 엔티티로 tts.speak 호출
위의 호출 결과: media_player.home_voice는 playing 상태를 나타내고 속성(attributes)도 채워지지만, 오디오는 전혀 나오지 않습니다. 로그에는 아무런 예외(exception)도 발생하지 않습니다. 이것이 가장 비용이 많이 드는 막다른 길인 이유는, 상태 머신(state machine)이 사용자에게 거짓 정보를 전달하여 절반만 작동하는 것처럼 보이기 때문입니다.
막다른 길 2: 즉각적인 확인(ack) + 비동기 응답 (두 번의 announce)
assist_satellite.announce로 전환하고 나면, 다음 본능은 대화의 매끄러움(conversational polish)을 추구하는 것입니다. 사용자가 자신의 말이 전달되었음을 알 수 있도록 즉시 "잠시만 기다려 주세요"라고 말한 뒤, 느린 작업(조수 간만 차 조회, LLM 왕복 등)이 완료되면 실제 답변을 말하는 방식입니다:
# 하지 마세요 — 첫 번째 작업이 완료되기를 기다리지 않고 두 번의 announce가 실행됨
- service: assist_satellite.announce
target:
...
문제는 타이밍입니다. 문서에 따르면 async_announce는 "장치에서 announce 재생이 완료되었을 때만 반환되어야 하며", 새틀라이트는 무언가가 tts_response_finished를 호출하여 다시 idle 상태로 돌려놓을 때까지 responding 상태를 유지합니다. 따라서 두 번째 announce는 새틀라이트가 첫 번째 작업으로 인해 여전히 responding 상태인 동안 도착하게 됩니다. 그리고 우리의 테스트 결과, 바로 이 지점에서 문제가 발생했습니다. 두 번째 announce가 누락되거나 새틀라이트가 먹통(wedge)이 됩니다.
이 중 어느 정도가 "문서화된 버그"이고 어느 정도가 "우리의 설정 문제"인지 정확히 짚고 넘어갈 가치가 있습니다. 왜냐하면 상위(upstream)의 상황은 처음 보이는 것보다 더 복잡하기 때문입니다:
- assist satellite가 "responding" 상태에서 멈추는 현상 (core #142363)에 대한 보고가 있습니다. 하지만 이는 정상적인 대화 후에 새틀라이트(satellite)가
idle상태로 돌아가지 못하는 문제에 관한 것이며, 계획되지 않음(not planned)으로 종료되었습니다. 따라서 이를 확정된 해결책이나responding상태로의 announce가 데드락(deadlock)을 유발한다는 증거로 해석해서는 안 됩니다. 이는 단지responding상태가 멈출 수 있다는 점을 보여줄 뿐입니다. - "스크립트에서 두 개의 assist_satellite.announce를 실행하면 실패함"이라는 커뮤니티 스레드가 있지만, 주의 깊게 읽어야 합니다. 해당 실패 사례는 음성 인텐트(voice intent)로부터 호출된 스크립트에 국한된 것이었으며, 진단된 원인은 동일한 장치에서
announce와set_conversation_response를 혼용했기 때문이지, 일반적인 상황에서 두 개의 announce가 충돌했기 때문이 아니었습니다. 동일한 두 개의 announce 스크립트도 자동화(automation)나 개발자 도구(Developer Tools)에서는 문제없이 실행되었습니다. 따라서 이는 주의해야 할 사항(gotcha)이긴 하지만, "두 개의 announce는 항상 충돌한다"는 것보다는 훨씬 좁은 범위의 문제입니다.
결론적으로, 저희의 문제를 해결해 준 신뢰할 수 있는 규칙은 새틀라이트가 idle 상태일 때만 단 한 번의 announce를 실행하는 것입니다. 해결 방법을 참조하세요. 저희는 "responding 상태로의 announce가 새틀라이트를 끼이게 만든다"는 점을 공식 문서의 상태 머신(state machine)과 일치하는 저희 설정의 동작 방식으로 취급하며, 상위(upstream) 프로젝트에서 버그로 분류한 것은 아닙니다.
막다른 길 3: 모든 것이 올바른데도 여전히 소리가 나지 않음
assist_satellite.announce를 사용하고, 단일 호출을 수행하며, idle 상태에서 실행했는데도 — 관련 없는 재시작 이후에 다시 무음 상태가 됩니다. 맨 처음 문제와 동일한 증상입니다: announce 전의 차임(chime)음은 들리지만, 그 후 음성이 나오지 않습니다. 서비스 호출(service call)을 다시 확인해 봅니다. 아무런 문제가 없습니다.
사용자의 호출 문제는 아닙니다. Piper 애드온 (add-on)이 충돌(crash)했고 다시 복구되지 않았던 것입니다. TTS(Text-to-Speech)에 엔진이 없으므로, 메시지가 합성되지 않아 아무런 음성도 나오지 않게 됩니다. TTS 호출 시 Piper가 충돌하는 현상은 저희만의 문제가 아닙니다: Piper: Crash after any TTS call (addons #3379)에서 정확히 이 현상을 보고하고 있습니다 — ConnectionResetError가 발생한 후 빈 오디오 경로에서 FileNotFoundError가 발생하는데, 즉 합성이 파일을 생성하지 못한 것입니다. Watchdog(감시 프로세스)이 없다면, 충돌한 Piper는 계속 다운된 상태로 유지되며 이를 가리키는 명확한 증상도 나타나지 않습니다.
차임(chime)에 관한 참고 사항:
assist_satellite.announce는 그 자체로 미디어 ID인 사전 알림 차임을 재생하며, Voice puck에는 기기 자체의 웨이크(wake)/피드백 사운드도 있습니다. 저희는 음성이 나오지 않는 동안에도 기기에서 어떠한 소리는 여전히 나는 것을 확인했으며, 이 점이 상황을 매우 혼란스럽게 만들었습니다. 하지만 특정 차임이 다운된 Piper 상태에서도 재생되는지 여부는 해당 차임이 어디에서 오는지에 따라 달라집니다. 너무 깊게 해석하지 마세요. 확실한 판별법은 아래에 있습니다.
해결 방법
세 가지 작은 조치 사항이 있습니다:
1. 새틀라이트 엔티티 (satellite entity)를 대상으로 assist_satellite.announce를 사용하세요:
service: assist_satellite.announce
target:
entity_id: assist_satellite.home_voice # 사용자의 puck의 assist_satellite 엔티티
...
본인의 엔티티 ID로 교체하세요. 이는 머신 로컬(machine-local) 값입니다. 개발자 도구(Developer Tools) → 상태(States)에서
assist_satellite.로 필터링하여 찾을 수 있습니다.
2. 한 번의 알림(announce), 유휴(idle) 상태에서만 수행하세요. 즉각적인 확인 응답(immediate-ack) 패턴은 버리십시오. 새틀라이트가 responding 상태일 때는 절대 하지 말고, 오직 유휴(idle) 상태일 때만 최종 응답과 함께 단일 announce를 실행하세요. 만약
가장 많은 시간을 아껴주는 진단 방법 하나는 다음과 같습니다: 음성이 무음이 된다면, 자동화(automation)를 건드리기 전에 Piper 애드온(add-on) 상태를 먼저 확인하세요. 음성은 Piper에 의존합니다. Piper가 다운되어 있다면, 호출 방식이 아무리 정확하더라도 announce나 tts.speak는 결코 오디오를 생성할 수 없습니다. Piper 애드온 로그(ConnectionResetError, 빈 경로에서의 FileNotFoundError)가 결정적인 단서가 됩니다. 오디오 경로가 정상이라는 증거로 차임(chime) 소리를 믿지 마세요. 아래의 주의 사항을 확인하십시오.
왜 중요한가 / 주의 사항 (gotchas)
- 상태 변화는 오디오 출력의 증거가 아닙니다. 새틀라이트(satellite)의
media_player엔티티에tts.speak를 실행하여 상태가playing으로 업데이트되었다고 해서, 실제로 소리가 나왔는지는 알 수 없습니다.assist_satellite장치의 경우,media_player엔티티가 아니라 직접 귀로 확인하거나(또는 새틀라이트 자체의 상태를 확인하거나) 하세요. - 퍽(puck)은 처음부터 새틀라이트입니다. ESPHome 통합(integration)은 Voice puck을 채택(adopt)하는 즉시
assist_satellite로 노출합니다. 로컬 STT (Whisper, Speech-to-Phrase 또는 Cloud)는 이와 관련이 없습니다. 따라서 첫날부터assist_satellite.announce를 대상으로 삼으세요.tts.speak-to-media_player 자동화는 이 장치에서 소리를 내지 못했을 수도 있으며, 단지 말을 해야 할 때까지 눈치채지 못했을 뿐일 수 있습니다. responding상태를 '사용 중'으로 취급하세요. 문서에 따르면, 새틀라이트는tts_response_finished가 이를idle로 되돌릴 때까지responding상태를 유지합니다.announce는 오직idle상태일 때만 실행하세요. 더 좋은 방법은, 가능한 경우 일반적인 대화 파이프라인(conversation pipeline)이 음성 응답을 반환하도록 하는 것입니다. 그러면 직접announce를 수동으로 만들지 않아도 음성이 새틀라이트를 통해 다시 흘러나옵니다. (참고로,responding상태 자체가 멈출 수 있다는 점을 유의하세요 — 이는 귀하가 하는 작업과는 무관한 코어(core) 이슈 #142363입니다.)- 차임(chime) 소리를 믿지 마세요. 음성이 완전히 죽어 있는 동안에도 장치의 무언가가 계속 차임 소리를 낼 수 있으며, 바로 이 점 때문에 TTS 엔진 대신 자동화를 디버깅하게 되는 것입니다. 특정 차임 소리가 Piper가 작동하지 않는 상황에서도 들리는지는 해당 소리가 어디에서 생성되는지에 따라 다르므로, 이를 근거로 판단하지 말고 Piper를 확인하세요.
결론
이 내용은 _Sailing Naturali_의 보트 에이전트 스택(boat-agent stack)을 위한 로컬 음성 프론트엔드(voice front-end)를 구축하는 과정에서 나왔습니다. _Sailing Naturali_는 모든 것이 전기 구동되는 차터용 카타마란(charter catamaran)으로, 조타석(helm)이 Home Assistant Voice puck을 통해 로컬 LLM(Large Language Model)과 대화합니다. 에이전트와 MCP(Model Context Protocol) 툴링은 github.com/sailingnaturali에서 오픈 소스로 제공되며, 음성 프론트엔드 관련 노트도 그와 함께 관리되고 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기