Triggerhappy を使って利用者で表示切り替えが可能なキオスク端末的なものを作ってみる

[しまねソフト研究開発センター](https://www.s-itoc.jp)（略称 ITOC）にいます、東です。

https://www.raspberrypi.com/software/ で配布されている RaspberryPi OS では、[Triggerhappy daemon](https://github.com/wertarbyte/triggerhappy) が標準で動作しています。しかし使っている人は少ないのか、情報も少ないようです。
そんななか、Triggerhappyを使ってみたらわりと便利だったので、少ない情報を少しでも補完すべく、今回 4キーのキーパッドを RaspberryPi に接続し、キーを押してウェブブラウザの表示URLを切り替えることを Triggerhappy を使ってやってみます。

# 目標・方針

* 4キーのキーパッドを使い、ウェブブラウザの表示を切り替える
* Triggerhappy daemon で、キー入力をひろう
* Chromeブラウザのデバッグポートを使い、URL切り替えを行う


# 準備するもの

## 1. キーパッド
何でも良いですが、私は手元にあった [meishi2キット](https://shop.yushakobo.jp/products/834) に、[ProMicro RP2040](https://www.switch-science.com/products/7441) を組み合わせ、[PRK Firmware](https://github.com/picoruby/prk_firmware) をインストールしたものを使いました。

![202412-2_IMG_3382.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/97d223e1-81e1-3155-dc88-c3a793600fed.jpeg)

## 2. ラズパイ

先日手に入れた、RaspberryPi 5 4GB を使いました。

![202412-2_Raspi5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/f75fb846-945b-b981-b16e-92e9e718b1d9.jpeg)


# 作業手順

## 1. RaspberryPi OS を SDカードへインストール

公式サイト https://www.raspberrypi.com/software/ より、Raspberry Pi OS with desktop をダウンロードし、適当な方法で SD カードへインストールします。
私は、2024-11-19-raspios-bookworm-armhf.img.xz をダウンロードし、FreeBSD マシンをつかって、dd で入れました。
```
xzcat 2024-11-19-raspios-bookworm-armhf.img.xz | dd of=/dev/da1 bs=1m
```

## 2. 初期設定

作成した SD カードをラズパイ実機に挿入し、起動します。
初期設定ダイアログが表示されます。私は以下の通り設定しました。

### Set Country

 Country: `Japan`
 Language: `Japanese`
 Timezone: `Tokyo`

### Create User

互換性を考慮して pi でユーザ作成しました。ただし、デフォルト値ですけどと注意されるので、別なものを付けるほうが無難かもしれません。

 Enter username: `pi`
 Enter password: `*****`
 Confirm password: `*****`

### Select WiFi Network

無線LANの設定ですが、今回は有線を使ったので、`Skip` します。

### Choose Browser

Chromium にマークをつけて `Next` をクリックします。

### Update Software

このタイミングでソフトウェアアップデートするかどうかです。
しておいた方が無難と思い、`Next` をクリックしました。

### Setup Complete

初期設定が終了しましたので、`Restart` で再起動します。


## 3. 使用ツール類のインストール

ブラウザ 「Chromium」は、最初から入っているものを使います。
Chromium のデバッグポートへ接続するためにRubyを使うので、Rubyと利用する gem をインストールします。

```sh
sudo apt install ruby
sudo gem install websocket-client-simple
```

## 4. URL変更スクリプトの作成

~/pi/bin ディレクトリを作り、chromium_change_url.rb ファイルを作ります。スクリプトには、実行属性をつけておいてください。

```ruby:/home/pi/bin/chromium_change_url.rb
#!/usr/bin/env ruby

require "json"
require "net/http"
require "websocket-client-simple"
require "timeout"

URLs = [nil,
  "https://www.ruby-lang.org/ja/",
  "https://mruby.org/",
  "https://github.com/mrubyc/mrubyc",
  "https://github.com/picoruby/picoruby",
]

begin
  url = URLs[ ARGV[0].to_i ] || URLs[1]
  exit if !url

  system("logger CHANGE URL=#{url}")

  # Debug port URL を取得
  res = JSON.parse( Net::HTTP.get("localhost", "/json", 9222) )
  raise "ChromeResultEmpty" if res.empty?

  page = res.find {|item| item["type"] == "page" }
  raise "PageNotFound" if !page

  debugger_url = page["webSocketDebuggerUrl"]
  raise "SocketNotFound" if !debugger_url

  # Debug port へ接続
  rcvq = Queue.new
  ws = WebSocket::Client::Simple.connect( debugger_url ) {|ws|
    ws.on(:message) {|msg| rcvq.push(msg.data) }
  }

  # openを待つ
  Timeout.timeout(10) { sleep 0.1 until ws.open? }

  # リクエスト送信
  command = 'Runtime.evaluate'
  params = {expression: "window.location='#{url}'"}
  msg_id = 1
  Timeout.timeout(10) {
    ws.send( {method: command, params: params, id: msg_id}.to_json )
  }

  # リザルト受信
  Timeout.timeout(10) {
    while res = JSON.parse(rcvq.pop)
      break if res["id"] == msg_id
    end
  }

rescue =>ex
  system("logger 'Error: #{ex}'")
end
```



### テスト
この段階で、手動でテストできます。
まず、chrome をデバッグポートつきで起動します。
```sh
chromium --remote-debugging-port=9222
```
先のスクリプトに、1から4のパラメータを付けて起動します。
```sh
bin/chromium_change_url.rb 1
```
スクリプトの URLs に指定したアドレスに変わったら成功です。


## 5. Triggerhappyの設定

Triggerhappyは、/dev/input/event に流れるイベントを見て、設定ファイルにマッチするイベントがあれば、指定されたコマンドを実行するというものです。
注意点は、マッチしたイベントは Triggerhappy に補足されても消えるわけではないことです。今回はキー入力イベントをとりますので、入力されても無害なキーを選定しないと、そのままブラウザ等にキー入力として渡ってしまいます。
いくつか候補は考えられますが、今回は、`ALT` + `数字` を使ってみます。

### Triggerhappyのキー識別子の確認

`--dump` 付きでthd を起動すると、各イベントが Triggerhappy からどう識別されるかが表示されるので、それを参考にします。
```
thd --dump /dev/input/*
```

今回の場合、`KEY_1`...`KEY_4` と、`KEY_LEFTALT` であることがわかりました。

### 設定ファイルの準備

設定ファイル `key.conf` を新規作成します。
```:/etc/triggerhappy/triggers.d/key.conf
KEY_1+KEY_LEFTALT	1	/home/pi/bin/chromium_change_url.rb 1
KEY_2+KEY_LEFTALT	1	/home/pi/bin/chromium_change_url.rb 2
KEY_3+KEY_LEFTALT	1	/home/pi/bin/chromium_change_url.rb 3
KEY_4+KEY_LEFTALT	1	/home/pi/bin/chromium_change_url.rb 4
```

### 起動ユーザの調整

デフォルトでは nobody ユーザーで起動していますので、このままではパーミッションの関係で不具合がでます。pi ユーザーで起動するように設定変更します。
systemd起動設定ファイルを、以下のとおり `--user` パラメータの後を `pi` に変更します。

```:/lib/systemd/system/triggerhappy.service
[Unit]
Description=triggerhappy global hotkey daemon
After=local-fs.target

[Service]
Type=notify
ExecStart=/usr/sbin/thd --triggers /etc/triggerhappy/triggers.d/ --socket /run/thd.socket --user pi --deviceglob /dev/input/event*

[Install]
WantedBy=multi-user.target
```

反映させるために、以下のコマンドを適用するか、もしくは再起動します。
```sh
systemctl daemon-reload
systemctl restart triggerhappy
```

## 6. ブラウザ自動起動

せっかくなので、ブラウザの自動起動ぐらいはしたいものです。
ざっと調べて、いくつか方法があるようですが、xdg autostart （名前あってる？）を使ってみます。

```:/home/pi/.config/autostart
[Desktop Entry]
Type=Application
Name=chrome
Exec=/usr/bin/chromium --incognito --kiosk --remote-debugging-port=9222 https://www.yahoo.co.jp
#OnlyShowIn=GNOME;
X-GNOME-Autostart-enabled=true
Comment=
```

## 7. キーパッドの準備

キーパッドへ、キー割り当て設定をします。初期設定は、[公式Tutorial](https://github.com/picoruby/prk_firmware/wiki/Tutorial) を見てもらうとして、キー設定ファイルは以下の通りとなりました。

```ruby:keymap.rb
#
# meishi2 + ProMicro RP2040
#

kbd = Keyboard.new

kbd.init_pins(
  [ 6, 7 ],   # row0, row1
  [ 28, 27 ]  # col0, col1
)
kbd.add_layer :default, %i[
  Key1 Key2 Key3 Key4
]

kbd.define_composite_key :Key1, %i[KC_LALT KC_1]
kbd.define_composite_key :Key2, %i[KC_LALT KC_2]
kbd.define_composite_key :Key3, %i[KC_LALT KC_3]
kbd.define_composite_key :Key4, %i[KC_LALT KC_4]

kbd.start!
```

これで終了です。
ラズパイを起動したら、自動的にウェブブラウザが全画面で立ち上がり、キーパッドの押下でURLが切り替わります。


# おわりに

4キーのキーパッドを使って画面の切り替えができる、キオスク端末的なものを作ることができました。Triggerhappyは、Linux全般で動くはずなので、応用範囲は広いと思います。


# 参考文献

https://qiita.com/yuji_miyano/items/ce1baea5d7deff803e1e