
2026/06/02 7:57
私の2つの照明スイッチが無限のエコーループに固まってしまいました。
RSS: https://news.ycombinator.com/rss
要約▶
日本語訳:
2時、Tasmota制御のMartin Jerry製壁式スイッチ2台がMQTTを介して互いに繰り返しトリガーされ、無限のオン/オフループに陥りました。根本原因分析の結果、状態ミラーリングプロトコルの故障が発見されました:各デバイスが
cmnd/<me>/Powerコマンドを受信し実行しながらもローカルVAR1(重複排除キー)を更新しなかったため、エコー増幅が発生し安定化には至らなかったのです。調査によりハードウェアの故障、起動タイミングの問題、ボタンによる曖昧さの可能性は否定されました。さらに10秒のリブート遅延を追加しても、その後の電圧の微かな変動が直ちにループを再発させるため不十分でした。物理的な押し操作とリレーの変化を区別しようとした試みも、一方のスイッチにTuya MCUが含まれており共有されたTuyaReceivedイベントが存在したため阻害されました。デバウンス調整も状態追跡の増幅を止めるのではなく単に遅らせるだけでした。
効果的な解決策は、ピア間の
PowerコマンドをEvent#SYNCに置き換えることであり、これは受動側がリレーを作動させる前にVAR1の状態マーカーを更新することを可能にしました。この単一のライン変更だけで無限ループは停止し、6回の連続トグルを含む迅速なコマンドシーケンスも不安定性なしに伝播するようになりました。重要なのは、設定を有効にする前に両方のスイッチを一致するリレー状態へ初期化し、VAR1にも適切にシード値を設定しておく必要があり、そうでないと直ちに再発する対立が起きることです。本解決策は、厳密なセットアッププロトコルがなければ自動化された照明やホームコントロールネットワークを無用化する危険な形態の状態増幅を排除します。本文
無限ループに陥ったスマートホーム照明スイッチの正体と解決策
問題の発覚
- 現象: 午前 2 時を回ると、廊下に「ON/OFF」と切り替わるリレーの音(クリック音)が永遠に響き続ける。
- 原因: 壁に取り付けられた 2 つの照明スイッチ同士が、状態変更を相互に転送し合うことで**無限ループ(ピョンポン現象)**を起こしている。
- ログ確認結果:
と.155
という通信プロトコル同士で、互いの Power コマンドを送り合い、OFF を指示し合うことが記録された。.166- デバイスは受信した変化を適用すると同時に再発表するため、システムは収束せず増幅され続けている。
問題の背景と初期アプローチ
- 環境: Tasmota + MQTT によるスマートホームスタック(Martin Jerry 製スイッチ)。
- 目的: 2 つのスイッチ間で状態を同期させ、「片方の操作で他方も追従する(仮想 3-way)」機能を実現。
- 初期実装(素朴なエコーガード):
- 状態変更時に新しい値を
に保存し、同じ値が来た場合は無視する仕組みを実装。VAR1
Rule1 ON Power1#State!=%var1% DO Backlog VAR1 %value%; Publish cmnd/<peer>/Power %value% ENDON - 状態変更時に新しい値を
- 一時的な成功: このロジックは長期間安定して動作し、「エレーガード」と呼ばれる機能として満足していた。
なぜ失敗したのか:2 つの発見
1. 理論と実態の不一致(起動競合ではない)
- 想定: 停電後の同時再起動による「起動競合」が原因だったかと考え、対策を試みた。
- 実測結果:
- 物理的なボタン操作を無視し、MQTT コマンドだけでループが発生していることを確認。
- 「起動安定化ガード(起動後 10 秒間ルールを非アクティブにする)」などの応急処置は通用しないことが証明された。
2. エコーガード自体の誤作動
- 根本原因:
を更新していないため、入ってくるミラーコマンドは常に「新規」と見なされ続けた。VAR1 - 詳細メカニズム:
- 相手が
を書き込む → 自社のリレーが切り替わる → だがPower
は更新されない。VAR1 - その結果、次に来る相手のコマンドはローカルの「陳腐な
」と不一致として判定される。VAR1 - このためルールが常時発火し、両スイッチは逆位相で永久に駆動し続ける状態になった。
- 相手が
ハードウェアの複雑さ:Tuya デバイスの壁
- 想定された解決策: ミラーリングを「物理的ボタン押下」のみに行い、MQTT コマンドによるリелей変更は無効にする。
- 障壁: Tuya 搭載デバイスの挙動により不可能だった。
- Tuya 側では、物理ボタン操作と MQTT コマンド操作の両方が、同じイベント (
) として返却される。dpId 1 - これらを区別できないため、「物理入力のみ反応させる」というロジックは実現不可能な状態(行き詰まり)に陥った。
- Tuya 側では、物理ボタン操作と MQTT コマンド操作の両方が、同じイベント (
最終的な解決策:キーの更新戦略
- 洞察: バグの原因は「リレーを更新するが、エコーを識別する
を更新していない」ことにあった。VAR1 - 方針: 相手が直接 Power コマンドを送るのではなく、イベント経由でリレー動作を依頼し、その都度
を再設定する。VAR1
修正後のルール
Rule1 ON Power1#State!=%var1% DO Backlog VAR1 %value%; Publish cmnd/<peer>/Event SYNC=%value% ENDON ON Event#SYNC DO Backlog VAR1 %value%; Power1 %value% ENDON
動作の仕組み
- 外部からイベントが到着すると、まずハンドラ内で
を設定する。VAR1 = value - 次にリレー (
) の状態を変更する。Power1 - これにより、直後の
とローカルのPower1#State
が一致するため、公開ルールの条件 (VAR1
) に適合しなくなっている(静寂化)。!= - エコーは 1 ホップ後に確実に消滅する。
検証結果
- テスト方法: 片方のスイッチで 6 回連続してオン/オフ操作を行い、双方のログを確認。
- 結果:
- 追従動作は 6 回発生したが、いずれも
のトリガーのみだった。EVENT#SYNC - 両方のスイッチが発火する自社の
は一度も行われなかった。Power1#State...Publish - オペレーション停止直後に、両スイッチの状态が静寂に収束した。
- 追従動作は 6 回発生したが、いずれも
得られた教訓
- デバッグの前向き: 修正をデプロイせずに「どうなるか」だけを想像してはならない。実際にログを見て検証する。
- 理論よりも現場: 「起動競合」といった仮説を検証するより、ログを 10 分間見ても本質を見極められる。
- デバウンシングの限界: タイミングの問題(高速操作)という誤解から陥りやすいが、これは状態追跡エラーであり、速度遅延では解決できない。
- 最小手数の修正: メカニズムを正しく理解した後に導かれるのは、往々にしてシンプルで最小限の解決策である。
結論:このクラスの問題への対処法
- 共通パターン: ノードが入ってくる変化を適用しつつも、自身のエコーを識別するためのキーを更新しない場合、ミラーリングは増幅器となり無限ループに陥る。
- 普遍的な例: スマートホーム照明、CRDT マージ失敗、Webhook の再試行ループ、イベントソースのフィードバックループなど。
- 解決の本質: 効果を適用する前に、重複除外キー(Dedup Key)を更新すること。
廊下は今や静かである。スマートホームエンジニアリングにおいて、これほど「高邁な評価」を得られたことはない。