🔑 重要なポイント
✦ AI·GEN筆者は Home Assistant の照明を音声で操作するため、クラウドを通らない方法を選んだ。home-assistant-matter-hub で HA のエンティティを Matter ブリッジとして包み、リビングの Google TV Streamer 4K をハブにする構成だ。ペアリングでは Google Home が2回「デバイスが見つかりません」と表示したが、原因はまったく別だった。1回目は、ブリッジが Matter のテスト用ベンダーコード 0xFFF1 を使っているため、先に Google Home Developer Console で VID/PID が一致する integration を作る必要があったこと。2回目は、サーバーの ufw に IPv4 の LAN 向けルールしかなく、ICMPv6 はデフォルトで許可されているので ping6 は通るのに、実際に IPv6 を使う UDP 5540 は捨てられていたこと。しかもペアリング後の接続は link-local を使うため、fe80::/10 も許可する必要がある。記事では各関門の確認方法と修正コマンドを載せ、ペアリング後の実際の使用感も記録している。照明はデバイスタイプを「照明」に変えて部屋を割り当て、音声でも部屋を言う必要があること。エアコンの温度デッドバンド設定でブリッジ全体がクラッシュしたが、RiDDiX がメンテナンスする fork に移行して解決したこと。エアコンは絶対温度でしか指定できないこと。
前回の記事で部屋の自己発電スイッチの照明を Home Assistant につなぎ、スマホから操作できるようになった。次にやりたいのは、ベッドに寝転んだまま音声で照明をオン・オフすることだ。
やり方は、home-assistant-matter-hub で HA のエンティティを Matter ブリッジとして包み、リビングの Google TV をハブにするというもの。経路はすべて LAN 内で完結し、クラウドを通らない。構築自体は難しくなく、時間がかかったのはペアリングのほうだった。Google Home がずっと「デバイスが見つかりません」と表示し続け、最終的に2つの問題が重なっていたことがわかった。1つは開発中デバイスに対する Google の制限、もう1つは僕自身のサーバーのファイアウォールだ。
以下、順番に整理する。なぜ Matter を選んだのか、matter-hub の設定、ペアリングで通過する関門、そしてそれぞれで詰まったときにどう確認してどう直したか。最後に、ペアリング後に音声操作を実際に使ってみて出てきた問題を記録しておく。
なぜ Matter なのか#
HA を Google Home につなぐ方法はだいたい3つある。
| Nabu Casa Cloud | 手動の google_assistant 連携 | Matter ブリッジ | |
|---|---|---|---|
| 費用 | 月額サブスク | 無料 | 無料 |
| 操作経路 | スマホ → Google クラウド → Nabu Casa → HA | スマホ → Google クラウド → 外部公開した自宅の HA | LAN 内で直結 |
| 外部公開 | 不要 | /api/google_assistant を Google から叩けるようにする | 不要 |
| ネット断で使えるか | 使えない | 使えない | 使える |
| 前提条件 | 有料 | Google Cloud プロジェクト、OAuth、サービスアカウント | 家に Matter ハブが必要 |
Matter ブリッジには Matter ハブが1台必要になる。最初は家にある SwitchBot Hub 3 で行けると思っていたが、公式の説明を読むと、あれはブリッジでしかなく、別途サードパーティのプラットフォームのホームハブが必要だとわかった。
それで一度は2番目の方法で行こうとした。そのあと、リビングの Google TV Streamer 4K がそもそも Matter ハブに対応していることを思い出し、3番目に戻した。クラウド不要、外部に何のエンドポイントも開けなくていいし、CrowdSec が Google のサーバーを誤ってブロックする心配もない。
matter-hub を立てる#
の元のプロジェクト(t0bst4r)は 2026 年 1 月にメンテナンスを終了してアーカイブされ、最終版は v3.0.4。現在は RiDDiX の fork が引き継いでいる。僕は最初は元のプロジェクトを使っていたが、クラッシュするバグのせいで fork に乗り換えた(後述)。これから構築するなら、最初から fork の image を使うのがおすすめだ。
services:
matter-hub:
image: ghcr.io/riddix/home-assistant-matter-hub:latest
container_name: matter-hub
restart: unless-stopped
network_mode: host # 必須。Matter は mDNS と IPv6 で探索する
environment:
- HAMH_HOME_ASSISTANT_URL=http://<HAのアドレス>:8123/
- HAMH_HOME_ASSISTANT_ACCESS_TOKEN=${HA_TOKEN}
- HAMH_HTTP_PORT=8482 # 管理画面
- HAMH_MDNS_NETWORK_INTERFACE=enp6s0 # ホストに NIC が複数あるときは、本当の LAN インターフェースに固定する
- TZ=Asia/Taipei
volumes:
- ./data:/data設定まわりの細かい点。
network_mode: hostは省略できない。Matter の探索は mDNS(UDP 5353)と IPv6 を使うので、bridge モードでは受け取れない。- 使うポートは 5353(mDNS)、5540/5541(Matter)、8482(管理画面)。ドキュメントにも開けるよう書いてある。
- トークンは HA の長期アクセストークンを使う。左下のアバター → セキュリティ → 一番下の「長期アクセストークン」。僕は
.envに置いて、パーミッションを 600 にした。 - 僕のホストには LAN 以外にも WireGuard、Incus のサブネット、NanoKVM の USB NIC、大量の docker bridge がある。インターフェースを限定しないと、ログに
wg0: send Unknown system error -126がひたすら出る。HAMH_MDNS_NETWORK_INTERFACEを LAN の NIC に設定したら消えた。これはログに影響するだけで、後のペアリング問題とは関係ない。
起動したら :8482 の管理画面で bridge を作る。入れたいのは照明2つだけなので entity_id で絞り込もうとしたが、ドロップダウンにその選択肢がなかった。
pattern に完全なエンティティ ID を入れれば完全一致になる。domain で switch を選ぶとすべてのスイッチが送られ、platform で esphome を選ぶと WiFi 信号や IP といった診断用センサーまで一緒に送られてしまうので、この2つは要注意。
保存するとペアリングコードと QR コードが生成される。Google Home アプリでの操作は + → デバイスのセットアップ → 既存のデバイスをお持ちですか? → Matter 対応デバイス。
ペアリングで通る関門#
まずペアリングの流れを分解しておくと、どこで問題が起きているのか後で照らし合わせやすい。
厄介なのは、2番目と3番目の関門のどちらで失敗しても、Google Home には同じ画面が出ることだ。
しかもどちらの場合も matter-hub のログは空っぽで、スマホがブリッジを見つけたのかどうかすらわからない。僕は両方の関門で躓いた。
第1関門:mDNS 探索#
最初に疑ったのはネットワークの分断だった。スマホと Google TV は 5GHz、サーバーは有線。ただ、ペアリングの段階で通信が必要なのはスマホとサーバーの間で、Google TV はペアリング完了後にハブとして引き継ぐだけなので、テレビがどの周波数帯にいるかはペアリングに影響しない。
スマホが本当にブリッジを見つけているかを確かめるには、LAN 上の mDNS を聞くのが一番手っ取り早い。サーバー上でマルチキャストグループに参加して 5353 を聞く小さなプログラムを書いたところ、結果はこうだった。
スマホ が _matterc._udp を問い合わせ ×30
サーバー の応答 ×30
matter-hub が受けた接続 0スマホは30回問い合わせ、サーバーも30回応答しているのに、スマホからの接続は一度もない。つまり第1関門は通っていて、問題はその先にある。
TIP
avahi が入っていれば、avahi-browse -rt _matterc._udp でもブリッジがアドバタイズしている内容が見られる。次の節で使う TXT フィールドも含めて。
第2関門:Google の VID/PID チェック#
サーバーの応答を分解してみると、TXT レコードに VP=65521+32768 がある。16進数に直すと Vendor ID 0xFFF1、Product ID 0x8000。0xFFF1 は Matter がテスト用に予約しているベンダーコードで、matter-hub のように CSA の認証を受けていないプロジェクトはみなこれを使っている。
Google の開発者ドキュメントによると、Google Home で開発中の Matter デバイスをペアリングするには、先に Google Home Developer Console で Matter integration を作り、VID と PID をデバイスと一致させる必要がある。テスト用の VID は、アライアンスが割り当てた 0xFFF1 から 0xFFF4 の中から選べる。
matter-hub がアドバタイズしているのは 0xFFF1 / 0x8000 で、僕のアカウントには integration が1つもなかった。
NOTE
matter.js のドキュメントには、Google は未認証デバイスに対して「未認証デバイスのペアリングを許可する」確認ダイアログを出すと書いてあるが、それは古いバージョンの挙動だ。僕が実際に遭遇したのは、確認ダイアログも何もなくいきなり「デバイスが見つかりません」が出るパターンだった。
登録の手順。
Google Home Developer Console に Google Home アプリと同じアカウントでログインし、プロジェクトを作成する。
Add Matter integration をクリック。初回は説明ページが出るので、Next: Develop、Next: Setup と進む。
Product name は好きに付けていい。Device type は Outlet(On-Off Plug-in Unit)を選ぶ。
Vendor ID は Test VID の 0xFFF1 を選び、Product ID に 0x8000 を入れる。ブリッジがアドバタイズしている値と一致させること。
保存してアプリに戻りもう一度スキャンすると、画面が「このデバイスが近くで検出されました」に変わった。これでこの関門は突破。
第3関門:IPv6 接続#
続行を押すと、また「デバイスが見つかりません」。
今回も matter-hub のログは空。まず1つの可能性を潰した。スマホが問い合わせを繰り返していたので、無線 AP が有線側のマルチキャストを捨てているのではと疑い、サーバーの応答をスマホに直接ユニキャストする小さなプログラムを書いて122回送ったが、やはり接続は来なかった。つまりスマホはレコードを確かに受け取っている。
残る未検証の部分は接続そのものだ。アドバタイズに載っているのは IPv6 アドレスだけで(Matter の仕様で IPv6 を使うと決まっている)、スマホはこのアドレスで UDP 5540 に接続する。以前、同じセグメントの NanoKVM から ping6 でこれらのアドレスを試したときはすべて応答があったので、ここはそれ以上調べていなかった。
そこで TCP で試すことにした。HA の 8123 をターゲットにして、同じマシン、同じアドレスに対して ping、IPv4、IPv6 でそれぞれ接続してみる。
ping -6 -c 3 <サーバーのIPv6アドレス>
curl -4 -m 5 -s -o /dev/null -w '%{http_code}\n' 'http://<サーバーのIPv4アドレス>:8123/'
curl -6 -m 5 -s -o /dev/null -w '%{http_code}\n' 'http://[<サーバーのIPv6アドレス>]:8123/'結果:
ping6 → 疎通OK(0.6ms)
IPv4 8123 → 200
IPv6 8123 → Connection timed outIPv6 の ping は通るのに、TCP はタイムアウトする。タイムアウトと connection refused は別物で、refused は相手が拒否したということ、タイムアウトはパケットが捨てられて何の応答もないということで、たいていはファイアウォールが原因だ。
原因:ufw の LAN 向けルールが IPv4 しかなかった#
このサーバーは を使っていて、外から入ってくる接続はデフォルトですべてブロックしている。1週間前、デスクトップから HA の画面に入れなかったときに、こんなルールを追加していた。
sudo ufw allow from 192.168.50.0/24 to any port 8123 proto tcp comment 'HA UI from LAN'送信元に IPv4 のサブネットを指定しているので、ufw は IPv4 のルールしか作らない。sudo ufw status verbose で見ると一目瞭然だ(抜粋)。
22/tcp ALLOW IN Anywhere
8123/tcp ALLOW IN 192.168.50.0/24 # HA UI from LAN
22/tcp (v6) ALLOW IN Anywhere (v6)送信元を指定していない Anywhere のルールにはすべて対応する (v6) があるのに、送信元を LAN に限定したルールには1つもない。言い換えると、このマシンは IPv4 では LAN に必要なポートを開けているが、IPv6 では LAN に対してポートを1つも開けていなかった。
ping6 が通るのは、ufw のデフォルトルールが ICMPv6 を許可しているから(IPv6 の近隣探索に必要)で、mDNS もデフォルトルールで許可されている。だから探索も ping も正常で、実際に接続を張る TCP と UDP だけがブロックされていた。そして Google Home はこの状況も「デバイスが見つかりません」として表示する。
直し方#
ルールを2つ追加して、LAN からの IPv6 も IPv4 と同じように入れるようにした。
sudo ufw allow from fc00::/7 comment 'LAN IPv6 (ULA)'
sudo ufw allow from fe80::/10 comment 'LAN IPv6 (link-local)'fc00::/7 は のプライベートアドレス、fe80::/10 は で、どちらもインターネットからルーティングされて入ってくることはない。追加したあと、IPv6 での 8123 への接続はタイムアウトから 200 に変わり、もう一度ペアリングしたら成功した。
fe80::/10 は省略できない。ペアリング成功後のログを見ると、スマホと Google TV が matter-hub と張った2本の接続は、どちらも送信元が fe80:: で始まる link-local アドレスだった。ULA だけ開けた場合、ペアリングが終わってもやはりつながらない。
WARNING
この2行は、LAN 内のすべての IPv6 デバイスがこのマシンのすべてのポートに接続できるようにするもので、僕が IPv4 でポートごとに開けている範囲より広い。うちの LAN は自分のデバイスしかないのでこうしている。LAN に信頼できないデバイスがあるなら、この2つの範囲に対して Matter 用の 5540/5541 だけを開ける手もある。ただし fe80::/10 は必ず含めること。
ペアリングしたあと#
ペアリング成功後、さらにいくつかデバイスを追加した。フロアランプ、ドアベルの投光器、エアコン、除湿機、空気清浄機、キャットタワーの電源で、最終的に8台。HA の script、automation、input_boolean は入れていない。Google Home ではコンセントの山になってしまうからだ。監視カメラのスイッチも、音声での誤作動が怖いので入れていない。
その後、3つの問題に当たった。
音声では必ず部屋を言う必要がある#
初めて Google に「電気消して」と言ったら、消えたのはフロアランプとドアベルの投光器で、部屋の照明はそのままだった。
部屋の照明2つは HA では switch なので、matter-hub はこれをコンセント(OnOffPlugInUnit)として送る。一方 Google の「電気つけて」「電気消して」は、デバイスタイプが照明のものにしか効かない。Google Home アプリでデバイスを長押し → 設定 → デバイスタイプを「照明」に変更し、さらに各デバイスに部屋を割り当てる。
変更したら今度は別の問題が出た。全部照明になったので、「電気つけて」とだけ言うと、Google はフロアランプも母の部屋の照明もまとめてつけてしまう。なので実際には毎回「OK Google、僕の部屋の電気つけて」と言っている。部屋まで言えば、その照明だけが動く。
エアコンでブリッジ全体がクラッシュ#
エアコンを追加したら、bridge 全体が落ちて再起動を繰り返し、他のデバイスも巻き込まれてオフラインになった。
climate_leng_qi.thermostat
minHeatSetpointLimit (1600) must be ≤ minCoolSetpointLimit (1600) − minSetpointDeadBand (200)
Failed to update bridge due to error: [constraint]Matter のサーモスタット仕様では、暖房の下限が冷房の下限より少なくとも 2°C 低くなければならないが、うちのエアコンはどちらの下限も 16°C だった。設定はすでに data/ に書き込まれているので起動するたびにこれに当たり、管理画面にも入れない。仕方なくまずコンテナを止め、使い捨てのコンテナで bridge の設定ファイルを編集してエアコンを外してから起動し直した。
元のプロジェクトはアーカイブ済みなので、もう修正されることはない。RiDDiX の fork は 8 月にサーモスタットの温度デッドバンドの扱いを修正していて(#435)、その後も僕と同じ症状の報告が出ている(#454「Thermostat with equal min_temp for heat/cool crashes entire bridge process」)。移行は image を fork のものに差し替えるだけで、data/ はそのままでいい。fabric と node の ID も変わらないので、Google Home で再ペアリングする必要もない。差し替えたあと、エアコンは RoomAirConditioner として認識され、問題なく追加できた。
エアコンは絶対温度でしか指定できない#
「エアコンを1度上げて」は反応がなく、ログはこうだった。
Invoke « thermostat.setpointRaiseLower
Validation-Error 0x80: Missing mandatory field mode in field modeこのコマンドは仕様上モードと調整量を含める必要があるが、どうやら Google が送ってくるパケットにはモードのフィールドが欠けているようだ。「エアコンを 26 度に設定して」は目標温度の書き込みなので正常に動くし、エアコンのオン・オフも問題ない。今のところはこれで使っている。
切り分けの順番#
表にまとめておく。「デバイスが見つかりません」に当たったら、上から順に確認していけばいい。
| 確認項目 | 確認方法 | 僕の結果 |
|---|---|---|
| スマホが mDNS の応答を受け取っているか | 5353 を聞く、または avahi-browse -rt _matterc._udp | 受け取っている、30問30答 |
| ブリッジが使っている VID | TXT の VP フィールド、65521 が 0xFFF1 | テスト用 VID |
| Developer Console に登録してあるか | 同じ Google アカウントで、VID/PID が一致 | 未登録、登録後「近くで検出」が出た |
| IPv6 の TCP/UDP が通るか | curl -6 で TCP サービスに接続する、ping だけで判断しない | ping は通る、TCP はタイムアウト |
| ファイアウォールに IPv6 の LAN 向けルールがあるか | sudo ufw status verbose で (v6) があるか見る | なし |
| link-local を許可しているか | fe80::/10 | 追加後にペアリング成功 |
今はベッドに寝転んだまま「OK Google、僕の部屋の電気消して」と言えば照明が消える。前回の RF スイッチとつながって、一通りの道のりは歩き終えた。ただし「部屋」の一言だけは省けない。
- home-assistant-matter-hub:RiDDiX がメンテナンスしている forkGitHub
- Google Home Developers:Matter integration を作成するdevelopers.home.google.com
- Google Home Developer Consoleconsole.home.google.com
まだコメントがありません
✨ 最初のコメントを残しませんか