コンテンツにスキップ

Zoom/Teams/Meet 対応のミュート用キーボードを作ってみた

meishi2

オンラインミーティングでスムーズにミュートの切り換えができるよう、自作キーボードの練習も兼ねて専用のキーボードを作ってみました。筐体はダンボールで。

SimPad Nano

キーアサインカスタマイズ可能な既製品(例:SimPad Nano)もあったのでその後購入してみました。こちらなら組み立ては不要です。

使ったもの

キーボードの組み立て

meishi2 keyboard ビルドガイド にとても詳しい手順がありますので、そちらを参考に組み立てます。

電子工作は苦手ですが問題なくできました。ありがたい!

ファームウェアのインストール

開発環境のセットアップ

こちらも手順に従ってセットアップします。

git clone https://github.com/qmk/qmk_firmware.git
cd qmk_firmware
git submodule update --init
./util/qmk_install.sh

補足

  • qmk_install.sh を実行すると、Homebrew で必要なパッケージが色々インストールされます。
  • meishi2 の手順ページには git submodule update --init がありませんが、これをしないと必要な submodule がダウンロードされず、ビルド時に tmk_core/protocol/lufa.mk:14: lib/lufa/LUFA/makefile: No such file or directory というエラーになってしまいました。(変わったのかも)

デフォルトのファームウェアをインストール

USB ケーブルでキーボードを接続して、以下のコマンドを実行します。

make meishi2:default:avrdude

「reset your controller now....」と表示されたら、リセットボタンを押すと書き込みが開始します。

キーマップを変更する

「default」を元に「mute」という名前のキーマップを作成します。

cp -Rp keyboards/meishi2/keymaps/default keyboards/meishi2/keymaps/mute

keyboards/meishi2/keymaps/mute/keymap.c を編集します。Simple Keycodes を参考に、各キーに割り当てるキーコードを指定します。左から「Esc(キャンセル)」「Cmd+D(Google Meetのミュート)」「Shift+Cmd+A(Zoomのミュート)」「Space(ZoomのPPT)」としてみました。

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  [0] = LAYOUT(KC_ESCAPE, LGUI(KC_D), SGUI(KC_A), KC_SPACE)
};

さきほどと同様に、インストールします。

make meishi2:mute:avrdude

Karabiner Elements でスクリプトを割り当て

単純なキーコードだけでは、以下の点で使いづらさがありました。

  • 対象のアプリケーションにフォーカスがないと動作しない。
  • アプリ毎にショートカットが違うので、間違って別の動作をおこなってしまう場合がある。
  • アプリケーションごとにボタンを使い分ける必要がある。

そのため、osascript で動作する以下のようなスクリプトを作成して、キーが押されたときにこれが実行されるようにしました。Zoom, Teams, Meet の順にアプリケーションが起動しているかチェックし、フォーカスしてからミュートをおこないます。

例) mute.scpt

#!/usr/bin/env osascript -l JavaScript

ObjC.import('stdlib')

function getApplication(name) {
  try {
    const app = Application(name)
    if (app.running()) {
      return app
    }
  } catch (e) {}
}

function muteZoom(key) {
  app = getApplication('zoom.us')
  if (!app) {
    return false
  }
  app.activate()
  if (key) {
    const se = Application('System Events')
    delay(0.1)
    se.keystroke(key, { using: ['command down', 'shift down'] })
  }
  return true
}

function muteTeams(key) {
  app = getApplication('Teams')
  if (!app) {
    return false
  }
  app.activate()
  if (key) {
    const se = Application('System Events')
    delay(0.1)
    se.keystroke(key, { using: ['command down', 'shift down'] })
  }
  return true
}

function muteMeet(key) {
  app = getApplication('Google Chrome')
  if (!app) {
    return false
  }
  let result = false
  app.windows().forEach((window, windowIndex) => {
    const tabIndex = window.tabs().findIndex(tab => tab.url().startsWith('https://meet.google.com/'))
    if (tabIndex >= 0) {
      app.activate()
      window.activeTabIndex = tabIndex + 1
      const se = Application('System Events')
      se.processes['Google Chrome'].windows[windowIndex].actions['AXRaise'].perform()
      if (key) {
        delay(0.1)
        se.keystroke(key, { using: ['command down'] })
      }
      result = true
    }
  })
  return result
}

function run(args) {
  const cmd = args[0]

  let result
  if (cmd == 'audio') {
    result = muteZoom('a') || muteTeams('m') || muteMeet('d')
  } else if (cmd == 'video') {
    result = muteZoom('v') || muteTeams('o') || muteMeet('e')
  } else {
    result = muteZoom() || muteTeams() || muteMeet()
  }
  $.exit(result ? 0 : 1)
}

ファームウェアで割り当てるキーは何でも良いので、以下のようにテンキーの 1〜3 とスペースを割り当てました。

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  [0] = LAYOUT(KC_KP_1, KC_KP_2, KC_KP_3, KC_SPACE)
};

Karabiner Elements の Simple modifications 設定で、各キーにスクリプトを割り当てます。「Target device」には「meishi2 (Biacco42)」を指定します。他のキーボードには影響しません。

Karabiner Elements 設定

↑のように shell_command への割り当ては GUI からは設定できないので、設定ファイル (.config/karabiner/karabiner.json) に直接以下のように記述します。(GUI で適当に割り当ててから修正すると間違えにくいと思います。)

{
    "profiles": [
        {
            "devices": [
                {
                    "disable_built_in_keyboard_if_exists": false,
                    "fn_function_keys": [],
                    "identifiers": {
                        "is_keyboard": true,
                        "is_pointing_device": false,
                        "product_id": 3,
                        "vendor_id": 48194
                    },
                    "ignore": false,
                    "manipulate_caps_lock_led": false,
                    "simple_modifications": [
                        {
                            "from": {
                                "key_code": "keypad_1"
                            },
                            "to": [
                                {
                                    "shell_command": "~/bin/mute.scpt audio"
                                }
                            ]
                        },
                        {
                            "from": {
                                "key_code": "keypad_2"
                            },
                            "to": [
                                {
                                    "shell_command": "~/bin/mute.scpt video"
                                }
                            ]
                        },
                        {
                            "from": {
                                "key_code": "keypad_3"
                            },
                            "to": [
                                {
                                    "shell_command": "~/bin/mute.scpt"
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}

プログラムからのキーボードを操作するには、macOS のセキュリティ管理上 Accessibility の権限が必要になります。そのため、System Preferences > Security&Privacy > Privacy の "Accessibility" に "karabiner_console_user_server" を追加しておきます。(自分の場合は適当に実行して何度か失敗していたら一覧に追加されたので、チェックを入れました。)

macOS Accessibility 設定

スクリプトを書き換えればなんでもできるようになりますね!(これなら専用ハードウェアも別にいらなかった気はします。)

お金あったら Stream Deck 欲しいです。