Claude Codeの通知を光で受け取る

IDチームの前田です。最近 Lautie Choc というフィジェットを購入し、手に伝わる振動と音で集中力を高めています。


今回は Claude Code の通知に気づかない問題を、マイコンの LED 点滅で解決した話です。



実装していることはマイコンの初歩中の初歩である Lチカ(LED 点滅) なので、「マイコンは初めて」という人にもおすすめです。部品は ATOMS3 Lite 本体だけで済み、はんだ付けも不要、コードも短いので、週末の趣味プログラミングとしてちょうどよいボリュームかと思います。Claude Code にお願いすると瞬殺ですが。

元々のアイデアは、元同僚の ku さんの SNS 投稿を見たのがきっかけです。blink(1) がなくても同じようなことができないかなと思ってやってみました。

3行まとめ

  • Claude Code のタスク完了通知を見逃す問題を、M5Stack の ATOMS3 Lite(1,700 円)で物理 LED 点滅化した話
  • 実装は Claude Code の hooks から HTTP リクエストを投げるだけ。マイコン側は初歩の Lチカ(LED 点滅)レベル
  • 通知には LED 点滅がおすすめ

課題: Claude Code の通知を見逃しがち

Claude Code で長いタスクを投げている間は他の作業をしていることが多く、応答が完了してもターミナルがバックグラウンドに埋まっていると気付けません。
macOS の通知バナーは一応出ますが、作業に集中しているとほかのアプリの通知に紛れてしまい、反応できていませんでした。

その結果、「タスクを投げて席を外す → 戻ったらターミナルを覗く → あ、終わってた」という流れが繰り返されていました。

ちなみに今は CCTOP というアプリも併用してメニューバーに常時通知を出し、複数の通知を組み合わせて活用しています。

ATOMS3 Lite とは

M5Stack が出している、500 円玉サイズの ESP32-S3 開発ボードです。

中身は ESP32-S3FN8 で、Wi-Fi と Bluetooth が内蔵されています。ボードには RGB LED(SK6812、GPIO35)1 個とボタン(GPIO41)1 個が最初から実装されており、給電と書き込みは USB-C 1 本で完結します。

通知デバイスとして向いている理由はシンプルです。小さいので机に置いても邪魔にならず、Wi-Fi 内蔵なので Mac とのケーブルも不要。電源さえ取れれば棚の上でも床でも置けます。LED 1 個でも、点滅が視界に入れば通知としては十分機能します。

ATOMS3 Lite を選んだ理由

日本国内ですぐに購入でき、技適もあり、1,700 円程度と比較的安価で要件を満たしていたため、これを選びました。

Wi-Fi 経由でアクセスするので、電源さえあれば置き場所は自由です。手元の Mac からしか叩けないということもなく、他のデバイスや将来別のホストからでも POST /notify を送れる構成になります。自前でファームウェアを焼く手間はかかりますが、その分、動作を細かく制御したい場合には都合が良いです。

ちなみに同じ M5Stack 系デバイス向けには、Anthropic 公式の claude-desktop-buddy というファームウェアもあります。

できあがったもの

全体の流れはこんな感じです。atom-notify.sh という curl の wrapper スクリプトを用意しました。
サンプルを公開しています。Claude Code にお願いすると、20 分くらいで動くところまで作業してくれると思います。

┌──────────────┐  hooks   ┌──────────────────┐  HTTP/Wi-Fi  ┌────────────────┐
│  Claude Code │ ───────→ │  atom-notify.sh  │ ───────────→ │  Atom S3 Lite  │
│    (CLI)     │          │      (Mac)       │              │   RGB LED      │
└──────────────┘          └──────────────────┘              └────────────────┘

                                                            ボタン押下で消灯

実際の動作としては、Claude Code がタスクを終えると Stop hook が発火し、Mac が POST /notify を ATOMS3 Liteに投げます。デバイスの RGB LED が赤く点滅しはじめるので、席を外して戻ってきても点滅が視界に入り、終わったことに気付けます。わかりやすいように赤にしていますが、LED の色は変更可能です。

席に戻ったら、次のプロンプトを送信するかデバイス本体のボタンを押すだけです。それだけで POST /clear が呼ばれて消灯し、IDLE 状態に戻ります。エンドポイントは /notify/clear の 2 つだけです。

Claude Code hooks の活用ポイント

hooks とは

Claude Code には、状態遷移のタイミングで任意のシェルコマンドを実行できる仕組みがあります。~/.claude/settings.jsonhooks フィールドに JSON で書くだけで動きます。公式ドキュメントは https://docs.claude.com/en/docs/claude-code/hooks を参照してください。

json
{
  "hooks": {
    "Stop": [
      {
        "matcher": ".*",
        "hooks": [
          { "type": "command", "command": "~/.config/claude/scripts/atom-notify.sh notify" }
        ]
      }
    ]
  }
}

Stop は Claude Code が応答を完了したタイミングで発火します。matcher には対象ツール名を正規表現で絞れますが、".*" にしておけば全件マッチします。

通知に使った 5 つのイベント

hooks で使えるイベントはいくつかありますが、今回の通知システムで実際に使ったのは 5 種類でした。「LED を点ける側」と「LED を消す側」に役割が分かれています。

点ける側は Stop(応答完了)と Notification(許可待ち発生)の 2 つです。消す側は UserPromptSubmitPostToolUseSessionStart の 3 つで、「ユーザーが操作に戻ってきた」ことを示す各タイミングで消灯させます。

イベントタイミング本記事での用途
Stop応答完了LED 点滅開始
Notification許可待ち発生LED 点滅開始
UserPromptSubmitプロンプト送信LED 消灯(席に戻ってきた合図)
PostToolUse (AskUserQuestion)選択肢 UI で回答LED 消灯
SessionStartセッション開始LED 消灯(初期化)

この構成で、「終わったら点く、触ったら消える」という動作になりました。

落とし穴 1: 選択肢 UI の回答は UserPromptSubmit を発火しない

Claude Code には、ユーザーに 1/2/3 や A/B/C を選ばせる選択肢 UI があります。AskUserQuestion ツールが裏で動いているタイプのものです。

当初は UserPromptSubmit だけを消灯トリガーにしていました。ところがこのイベントは、メインプロンプト欄からテキストを送信したときにしか発火しません。選択肢を選んで答えても UserPromptSubmit は飛ばない仕様でした。気付かないと、選択肢に答えても LED が点きっぱなしになります。

PostToolUsematcher"AskUserQuestion" を指定して別途拾うことで解決しました。

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "AskUserQuestion",
        "hooks": [
          { "type": "command", "command": "~/.config/claude/scripts/atom-notify.sh clear" }
        ]
      }
    ]
  }
}

落とし穴 2: macOS の atom.local は毎回 5 秒待たされる

デバイスの mDNS ホスト名 atom.localcurl でそのまま叩くと、毎回 5 秒前後ブロックされます。macOS の getaddrinfo が mDNS とユニキャスト DNS を並行クエリする仕様で、ユニキャスト側のタイムアウトを必ず待つためです。

  • --connect-timeout 1 を付けて回避しようとしましたが、これだと 1 秒で無音失敗します——IP は引けていないので当然でした。

結局、dns-sd コマンドで IP を直接取得してから curl --resolve で送るラッパーを書いて解決しました。

まとめ

  • 通知を受け取る手段としては、物理 LED が一番分かりやすかったです
  • Claude Code の hooks は応答完了・許可待ち・操作再開などのイベントを細かく拾えるため、こうした物理デバイス連携と相性が良いです
  • Claude Code にお願いすると、ファームウェアからシェルスクリプトまで短時間で実装してくれるので、物理デバイス連携のハードルがぐっと下がっています。夢が広がりますね

参考

この記事をシェア