Wiiリモコンを「左手デバイス」にする ― マクロパッドアプリ KUNAI を作った話

みなさん、こんにちは。

家の中にひとつは転がっているであろうWiiリモコン。Wiiの全盛期から時は流れ、今や現役を退いて押し入れに眠らせているという方も多いのではないでしょうか。「もったいないなあ」と感じてしまいますよね。

当サイトでも、以前Wiiリモコンを簡易的な左手デバイスとして活用する記事を公開したところ、ありがたいことにそれなりの反響をいただきました。

ただ、以前紹介したアプリは「設定を変えるたびにアプリの再起動が必要」「レイヤー機能がない」、そして何より「普段使いしているLinux環境で使えない」といったもどかしさがあり、だんだんWiiリモコンの出番が減ってしまっていました。

「これではいけない、自分で作ろう!」 と一念発起。

今回は、Wiiリモコンをキーボードショートカットやマクロを送信できる「レイヤー機能つき左手デバイス」に生まれ変わらせるアプリ KUNAI を開発し、公開しましたのでその開発記録をご紹介します。もちろん、WindowsとLinuxの両方に対応しています!


なぜ、このツールを作ったのか

左手でキーボードのショートカットを押し、右手でマウスを操作する、PC作業中はおなじみのスタイルですよね。

ファイル保存、元に戻す、タブの切り替え、音量調整……。よく使う操作ほど指を伸ばす位置がバラバラで、地味にストレスが溜まります。かといって、専用のマクロパッドを買い足すほどでもない。

そんなときこそ、Wiiリモコンの出番です。

  • Bluetooth で PC にワイヤレス接続できる
  • 十字キー、A、B、+、-、Home、1、2 と、左手デバイスにちょうどいいボタン数
  • 片手にすっぽり収まる抜群のグリップ感
  • 中古なら数百円〜数千円で手に入る手軽さ

「ショートカットを割り当てたいだけなのに、専用ハードを買うのはちょっと……」とモヤモヤしている方にとって、手元にある資産を活かすこのアプローチは、かなり有効ではないでしょうか。


作ったツール「KUNAI」の概要

名前の由来は、忍者の道具である「苦無(くない)」から。小さくて、さまざまな用途に使える便利な道具、というイメージで名付けました。

操作画面はシンプルに1枚のウィンドウにまとめ、接続状態・プロファイル・ボタン割り当て・ログを一覧できるようにしています。「Connect」ボタンを押して、Wiiリモコンの 1 ボタンと 2 ボタンを同時押しするだけで簡単に接続できます。

KUNAI画面
KUNAI画面

主な特徴

  • モーメンタリーレイヤー機能
    • B ボタンを押している間は Layer1、Home ボタンなら Layer2、両方押せば Layer3 に切り替えます。
    • QMK キーボードファームウェアでおなじみの「モーメンタリーレイヤー」と同じ考え方で、ボタンを離せば瞬時に元のレイヤーに戻ります。
    • これにより、10数個のボタンで数十種類ものショートカットを使い分けることが可能です。
  • 多彩な割り当てアクション
    • ショートカットキー (Ctrl+S, Alt+Tab など)
    • 標準アクション(ブラウザ起動、ターミナル起動、音量調整、ミュート、画面ロック、ブラウザの戻る・進む)
    • Delay(300) などを挟める柔軟なマクロ実行
  • プロファイル管理
    • JSON ファイルで管理し、default、coding、writing、browser の4つのプロファイルを用意。起動時に自動読み込み、終了時に自動保存します。
  • 技術スタック
    • .NET 8 LTS & C# 12
    • Avalonia 11(Windows 11 と Linux の両方でネイティブに動作するクロスプラットフォーム UI フレームワーク)
    • CommunityToolkit.Mvvm
    • OS へのキー送出は、Windows では SendInput、Linux では uinput を採用

AIと一緒に、小さく作って、必ず動かす

今回の開発は、Claude Code と二人三脚で進めました。

最初に「アーキテクチャ、レイヤーの動き、各クラスの責務、画面構成」を盛り込んだ詳細な仕様書を作成し、以下のステップで堅実に開発を進めました。

  1. 画面のモックアップを最初に作る
  2. 全体の設計(構成図、ソリューション構成、クラス図、ドメインモデル、インターフェース)を固める
  3. 実装は細かく1ステップずつ進め、その都度ビルドとテストを行う

全部で18のステップに分け、「次はステップ7に進んで」と指示しながら LayerManager、ProfileRepository、MacroExecutor、キー送出処理、ViewModel、XAML、DI、テストと順に組み上げていきました。

機能を小さく区切ることで、間違いに早く気づくことができます。特に、1ステップごとにビルドとテストを確実に通してから次に進む手法がものすごく効果的でした。


仕様書のなかの小さな矛盾と、テストが見つけてくれたバグ

仕様書をコードに落とし込んでいくと、どうしても細かい食い違いが出てきます。今回は主に3つの矛盾を解消しました。

  1. 依存関係の向き
    • 「UI → Core → Input → Platform」という方針と、「Core は Platform を参照しない」という原則を両立させるため、Core を中心に据えて Input や Platform 側が Core の抽象を実装する依存性逆転の原則を徹底しました。
  2. アクションの型
    • 「アクションは文字列」と定義した一方で、マクロは配列で割り当てたいという矛盾があり、Macro という専用フィールドを追加して存在時はそちらを優先する仕様に整理しました。
  3. 実行器の循環参照
    • アクション実行器がマクロ内からもアクションを呼び出す構造になっていたため、マクロ側から実行器を呼び出す一方向のフローに修正しました。

こうした設計判断の経緯は、すべて docs/architecture.md に理由つきでドキュメント化しています。


テストが本物のバグをキャッチ

ユニットテストは最終的に395件まで増えました。その中で、テストがしっかりと「本物のバグ」を見つけてくれた瞬間がありました。

標準アクションの名前を列挙型(Enum)に変換する際、Enum.TryParse を使っていたのですが、ショートカットとしてキーボードの 1 を割り当てたところ、これが数値として解釈されてしまい、意図せず「ターミナル起動」アクションとして誤動作してしまったのです。これは仕様通りの厳密な名前一致に書き換えることで解決しました。

また、電池残量の通知テストがときどき不安定になる問題もありました。値の更新とイベント発火のタイミングがズレていたのが原因で、イベントの完了を待ってからアサートする形に修正し、安定してパスすることを確認しています。


実機がない環境での工夫 & 実機でぶつかった壁

開発の母艦は WSL2 上の Linux 環境でしたが、ここには当然、Wiiリモコンも Bluetooth も物理画面もありません。また、Claude Code のオートモードで開発中に物理的に異なるデバイスとテストを行うのは無理があります。そのため、以下のような工夫で乗り切りました。

  • Wiiリモコンとの通信はインターフェースで抽象化し、テスト用のモック(Fake)を用意
  • キー送出処理も「データ構築部」と「OSへの送信部」に分離し、前者を徹底的にテスト
  • UI画面はヘッドレス(仮想フレームバッファ)で描画してスクリーンショットを自動撮影し、表示状態を確認

とはいえ、これで確認できるのは「頭で描いた通りに動くか」まで。実機での動作確認は別問題です。実際に試す中で、大きな壁が2つ立ちはだかりました。


Windows 11 での PIN 認証の壁

Windows 11 の設定画面から普通にペアリングしようとすると、Wiiリモコン側とのペアリング時に PIN(暗証番号)を求められます。しかし、Wiiリモコンには固有の PIN 入力キーなどありません。

「コードを使わずにペアリングする」などの裏技も試しましたがうまくつながらなかったため、「アプリ自身がペアリングをハンドリングする」 という方針に切り替えました。

  • Windows の Bluetooth API を使って周囲の Wiiリモコンをスキャン
  • Wiiリモコンの Bluetooth アドレスから適切な PIN を自動計算して渡す
  • HID サービスを有効化して、OSにコントローラーとして直結させる

結果として、アプリの「Connect」ボタンを押して Wiiリモコンの 1 + 2 を押すだけで、面倒な手順なしで一発につながるようになりました!


Linux では「そもそもペアリングしない」

Linux 環境でも、BlueZ を使った通常のペアリングでは苦戦しました。

そこで発想を転換し、「そもそもペアリング(認証)をしない」 というアプローチを取りました。Wiiリモコンは、HID の通信チャネルに接続する際に強固な認証を必須としていません。L2CAP(Bluetooth の論理リンクコントロールおよびアダプテーションプロトコル)を使えば、ダイレクトにソケット接続できます。

  1. bluetoothctl で数秒間周囲をスキャンして Wiiリモコンの MAC アドレスを取得
  2. 制御用と割り込み用の2本の L2CAP チャネルに直接接続
  3. 流れてくるレポートパケットの先頭バイトを適切にハンドリングして Windows 版と同じ入力データとして扱う

この仕組みにより、BlueZ のペアリングトラブルを完全に回避できました。こちらも実機での動作確認がバッチリ取れています(※あらかじめOS側でペアリング済みの状態だと接続を阻害する場合があるため、ペアリング解除をお忘れなく!)。


注意点と使い方

まだ公開したばかりの v0.2.0 ですので、以下の点にご留意ください。

  • 動作確認済み項目 ⇒ ペアリング、ボタン入力検知、キー送出(Windows / Linux 両対応)、電池残量取得
  • 未確認・今後の項目 ⇒ 画面ロック、Linux 環境での外部コマンド実行(ブラウザ起動や音量調整など)、Wayland 環境での挙動
  • Windows の警告 ⇒ コード署名を行っていないため、Windows の SmartScreen 警告が表示される場合があります。

導入の流れ

  1. リリースページ(v0.2.0 Releases)から、Windows 用なら KUNAI-win-x64.zip、Linux 用なら KUNAI-linux-x64.tar.gz をダウンロードします。
  2. 展開して、Windows なら KUNAI.UI.exe、Linux なら ./KUNAI.UI を実行します。
  3. 「Connect」を押し、Wiiリモコンの 1 と 2 ボタンを同時押しします。

.NET ランタイムは同梱されているため、別途インストールは不要です。なお、Linux でキーイベントを送出するには /dev/uinput への適切な権限が必要になります。詳しい設定手順はリポジトリの README.md や、同梱されている udev ルール(tools/linux/99-kunai.rules)をご参照ください。


これからについて

現時点ではジェスチャー認識機能はあえて実装していません(左手デバイスとして使う上で、具体的な利用シーンがピンとこなかったためです)。

ただ、Wiiリモコンは内蔵の加速度センサーを持っているので、「リモコンを振る動作」に便利な作業を割り当てられたら面白そうだなとは考えています。

「こんなボタン割り当てがあると便利」「こういう環境で動かしてみたよ」といったフィードバックや、不具合報告などは大歓迎です! GitHub の Issue や Pull Request にお気軽にどうぞ。

本日も最後までお読みいただきありがとうございました。

それでは、よい左手デバイスライフを!


「自分も何か作ってみたい」と思われた方、そのアイデア、頭の中で寝かせておくのはもったいないかもしれません。

ビューローみかみでは、構想段階の壁打ちからPoC・実装・現場導入まで、現場で「使い続けられる」ものづくりを支援しています。

アイデアを短期間で「動くもの」にし、PoCで終わらせず、実運用まで伴走します。まずは「これ、作る価値ありますか?」という壁打ちからでも大歓迎です。

▶ PoC・MVP開発サービスの詳細はこちら

気になることがあればお気軽にご相談ください。

カテゴリ: ハードウェア
上部へスクロール