現在、自宅サーバーにはスマートホームを実現するためのFOSS、HomeAssistantが導入されている。しかし、私はスマートスピーカーを持っていない。
そこで、自宅サーバーにスピーカーフォンを接続して、スマートスピーカーのようにHomeAssistantを操作できるようにした。
その作業メモです。
動機
自宅にメイドさんが欲しい。期待する用途は以下の通り。
- 音声指示でデスクライト等のスマートデバイスを操作する
- 各種HomeAssistantから利用できる値を元に、特定のシチュエーションで通知する
- 二酸化炭素モニタの値を元に、CO2濃度が高まった時に音声で警告する
- 天気が怪しくなったら、音声で警告する
- 生活をメイドさんに管理してもらいたい
- モーニングコールや就寝時刻など
必然的に、使用する音声はAlexaやGoogle Assistantのようなものではなく、可愛らしい声が望ましい。
背景
HomeAssistant(HAOS)はNoelleというホスト名のVM上で動作している。
下調べ
あと数日待てばブラックフライデーのセールでAmazon Echoとかが安く買えそうではあった。しかし調べた感じ、確かにHomeAssistantへの接続はできるものの、機能をフルに使うことはできないようだった。
それに、大企業の束縛から逃れるというのがHomelabをやっている理由のひとつなのに、Amazonの製品に頼るというのは哲学に反する。
AmazonやGoogleの経済圏に束縛されないスマートスピーカーや自作キットを使って実現する方法もあるようだが、なかなかいいお値段がする。
以下の動画はラズパイを使って実現したものだが、かなり自分のやりたいことに近い。
しかし、ラスパイもまた、なかなかいいお値段がする。既存の道具を有効活用して実現したい。
そこで、「自宅サーバーにマイクとスピーカーを接続し、ネットワークスピーカーとして機能するVM(LXC)を用意する」というアイデアを考えた。

作業メモ
以下、Geminiさんに作業記録から書き起こしてもらったもの。
構成概要
今回のゴールは、Proxmox上のLXCコンテナを「マイクとスピーカーのインターフェース(サテライト)」として機能させ、頭脳であるHome Assistant(VM)と連携させることだ。
- Hardware:
- Server: 自宅サーバー (Proxmox VE)
- Audio: Jabra Speak 410 (USB接続のスピーカーフォン)
- Software:
- Host: Proxmox VE 8.x
- Satellite: LXC Container (Debian 12) + Wyoming Satellite
- Brain: Home Assistant OS (VM)
- STT: Whisper
- TTS: VOICEVOX
- Wake Word: openWakeWord
- LLM: Google Gemini
1. ProxmoxホストとLXCの準備
まず、Proxmoxホストに接続したUSBスピーカーフォン(Jabra)を、LXCコンテナから認識させる必要がある。 セキュリティを考慮し、非特権コンテナ (Unprivileged Container) のままデバイスをパススルーすることに拘った。これが最初の難関だった。
1.1. デバイスの確認
Proxmoxホスト側でデバイスを確認。
ls -l /dev/snd/Jabraを接続すると controlC1 や pcmC1D0p などが増える。しかし、非特権コンテナではホストのデバイス権限(通常 root:audio)にアクセスできない。
1.2. udevルールによる権限の自動書き換え
「コンテナ内のroot」は「ホスト側のUID 100000」にマッピングされている。そこで、サウンドデバイスが接続されたら自動的にグループを 100000 に変更するudevルールを作成した。
/etc/udev/rules.d/99-lxc-audio.rules を作成:
SUBSYSTEM=="sound", GROUP="100000"ルールを適用:
udevadm control --reload-rulesudevadm triggerこれで /dev/snd/* の所有グループが 100000 になり、非特権コンテナから見える準備が整った。
1.3. LXCコンテナの設定
LXCの設定ファイル (/etc/pve/lxc/105.conf 等) に、デバイスのパススルー設定を追記。
サウンドデバイスのメジャー番号は 116 だったので、以下のように記述。
lxc.cgroup2.devices.allow: c 116:* rwmlxc.mount.entry: /dev/snd dev/snd none bind,optional,create=dirコンテナを再起動し、コンテナ内で aplay -l を実行して Jabra (Card 1) が見えていれば成功。
2. Wyoming Satelliteの構築 (LXC)
サテライト側のソフトウェアには、Home Assistant公式が推進する Wyoming Satellite を使用する。
2.1. インストール
公式のセットアップスクリプトを使用するのが最も確実だった。
git clone https://github.com/rhasspy/wyoming-satellite.gitcd wyoming-satellitescript/setup2.2. サービス化 (systemd)
ここが最大のハマりポイントだった。
最初はパイプを使った複雑なコマンドを書いていたが、最終的には公式スクリプト (script/run) を素直に使うのが正解だった。
また、サーバーのCPU (Core i3-7100) がAVX命令セットの関係でローカルでのウェイクワード検出ライブラリと相性が悪かったため、**「音声ストリームを垂れ流して、サーバー(HA)側でウェイクワードを検出する」**構成を採用した。
/etc/systemd/system/wyoming-satellite.service:
[Unit]Description=Wyoming Satellite for Home AssistantWants=network-online.targetAfter=network-online.target
[Service]Type=simple# Jabraは plughw:1,0 として認識されていたため明示的に指定ExecStart=/root/wyoming-satellite/script/run \ --name "Jabra Speaker" \ --uri "tcp://0.0.0.0:10700" \ --mic-command "arecord -D plughw:1,0 -r 16000 -c 1 -f S16_LE -t raw" \ --snd-command "aplay -D plughw:1,0 -r 22050 -c 1 -f S16_LE -t raw" \ --snd-volume-multiplier 1.0WorkingDirectory=/root/wyoming-satelliteUser=rootRestart=alwaysRestartSec=1
[Install]WantedBy=multi-user.target3. Home Assistant側の設定
HA側で「耳」「口」「頭脳」を組み立てていく。
注: ここまでで自宅サーバーの仮想Wyoming Satellite化が完了しているので、あとは基本的に上述の動画と同じようにやれば良い。
3.1. アドオンの導入
以下の公式/コミュニティアドオンを導入した。
- Whisper: 音声認識 (STT)。モデルは
base-int8、言語はja。 - VOICEVOX: 音声合成 (TTS)。HACSで統合を入れ、別途Docker(アドオン)でエンジンを立てる構成。ずんだもんや四国めたんなどが使える。
- openWakeWord: ウェイクワード検出。サテライトから送られてくる音声ストリームから「Okay Nabu」などを検知する。
※ 構築中にHACSのサーバーダウンに遭遇するという不運もあったが、復旧を待って解決した。
3.2. Wyoming Protocol 統合
「設定」>「デバイスとサービス」から Wyoming Protocol を追加。
LXCコンテナのIPアドレスとポート(10700)を指定すると、Jabra Speaker が認識される。
3.3. アシストパイプラインの作成
「設定」>「Voice assistants」で新しいパイプラインを作成。
- 言語: 日本語
- 会話エージェント: Google Generative AI Conversation (Gemini)
- STT: Whisper
- TTS: VOICEVOX TTS
- ウェイクワード: openWakeWord
「Proxmox Speaker」デバイスの設定で、このパイプラインを使用し、ウェイクワード検出を**「Home Assistantで処理」**に設定することで、常時ストリーミング構成が完成する。
4. LLMによる「人格」の形成
会話エージェントに、Google Gemini (API) を使用した。 標準のHome Assistantエージェントだと「電気をつけて」等の命令しか聞かないが、LLMなら雑談も可能になる。
システムプロンプト(Instructions)に以下のような設定を入れ、キャラ付けを行った。
あなたは「原神」の「ノエル」として振る舞ってください。常に献身的で丁寧な言葉遣いを心がけてください。5. モーニングコールの自動化
仕上げに、毎朝決まった時間に挨拶をするオートメーションを作成。 Geminiにセンサー値(天気やCO2濃度)を渡して「一言コメント」を生成させ、それをVOICEVOXで喋らせるというハイブリッド構成にした。
オートメーションの例 (Action部分):
- Conversation: Process
- Geminiに「天気とCO2濃度に基づいた一言コメント」を考えさせ、変数
noelle_commentに格納。
- Geminiに「天気とCO2濃度に基づいた一言コメント」を考えさせ、変数
- Assist Satellite: Announce
- ターゲット: Jabra Speaker
- メッセージ:
おはようございます、栄誉騎士様。現在の気温は {{ state_attr('weather.forecast_home', 'temperature') }} 度。お部屋の二酸化炭素濃度は {{ states('sensor.co2_sensor') }} ピーピーエムです。{{ noelle_comment.response.speech.plain.speech }}
以上、Geminiさん書き起こし終わり。
Future work
とりあえず期待していたものは作れたが、以下の点は改善したい。
ローカルLLMの採用
脱Google、脱Amazonの観点から言うと、ローカルLLMの採用が望ましい。サーバーにGPUが積んであればLlamaあたりを動かしたかった。普段使いのPC上でLLMサーバーを用意するのも考えたが、システムが複雑になりすぎるし、普段使いPCの電源を落とせなくなってしまう。
ウェイクワード検出の改善
現状は「OK,Nabu」で起動するが、HomeAssistantをノエルさんに見立てている以上、「ノエル」をウェイクワードとするべき。
これは一番上の動画を見る限りすぐに実現できそうな雰囲気。
音声の改善
同じ理由で、ノエルさんの声で話すようにしたい。技術的には実現できそうだが、お金、応答速度、倫理的な問題が生じる。
とはいえVOICEVOXの声も十分かわいいので、このままでも良いかも。
(VOICEVOX 中国うさぎ(おどろき)を採用。)
もっとスマートデバイスを増やす
現状、HomeAssistantに接続されているデバイスはCO2濃度計(温度・湿度も一応測れる)とデスクライトくらい。もっと増やしたい。
もっと大きな家に住む
小さな部屋で自動化してもありがたみが薄い。
まとめ
LXCの権限周りや、Home Assistantのパイプラインの仕組み(特にウェイクワードの検出場所)の理解に苦労したが、結果として非常に満足のいく「自作スマートスピーカー」が完成した。
既存のリソースを活用し、プライバシーを重視しつつ、いい感じの声と人格で生活をサポートしてくれる環境をほぼゼロ円で構築できた。