家族の予定は基本的に共有カレンダーで管理しているが「みんなが見れる用」として食卓近くに卓上アナログカレンダーが置いてあり、何年もの間、二重管理になっていた。ずっと気になってはいつつも、まぁ運用できてるし、単にカレンダー表示するだけでもまぁまぁ面倒なのでそのままだったが、この度 Raspberry Pi でサイネージを作ることにした。

構成はシンプルで、15インチの 1080p モニタと、余っていた Raspberry Pi 3 B (WiFi付き)

CUIUIC モバイルモニター 15.6インチ ポータブルモニター モバイルディスプレイ 1920x1080FHD IPS液晶パネル 400Nits 100%広色域 Tpye-C/mini HDMI/スピーカー内蔵 PS4/PS5/XBOX/Switch/PC/Macなど対応 保護カバー付き - CUIUIC

CUIUIC

4.0 / 5.0

できること

  • Google Calendar の表示 (ほぼ Google Calendar互換の表示をする。複数日とかも)
  • 天気予報 (気象庁のXMLから取得している)
  • 時計 (西暦・和暦・日付曜日時間)

10分ごとに内容を更新する。

https://github.com/cho45/my-sinage

モニタ解像度

Raspberry Pi のほとんどは 1080p までしかスムーズに出力できないので、あまり高精細なディスプレイを買ってはいけない。1920x1280 のモバイイルディスプレイをいくつか持っているけど、モニタとの相性もあるのか、まともに表示させることができなかった。素直になってください。1080pのモニタを買えばいいです。(2日ぐらい無駄にした)

15インチのモニタは微妙にデカいけど、カレンダーをフルで表示させるならこのぐらい大きくてもいいかなという印象。捨てるときのこと考えるとあんまり大きいもの買いたくないのよね……

モニタの固定

「モバイルモニタ」カテゴリのものは固定方法が考えられてない。VESAマウント? そんなものはない。ただのスレートです。

ということで3Dプリンタでスタンドを作り、棚板にビス止めすることにした。安定。

ウェブアプリ

認証情報を保持してカレンダーの画面を出すウェブサーバを立てている。これは Raspberry Pi 上ではなく、QNAP 上の Container Station で docker image を動かしている。QNAP はうちだとIP アドレス固定で使っているので管理しやすい。

ディスプレイが繋がっているサイネージ用の Raspberry Pi はブラウザでURLを開くだけ。

スマフォとかから管理画面を開いて強制リロードのシグナルを送ったりできるようにしてある。ウェブアプリ側を更新したときに呼ぶ用。

開発

ほぼほぼ Claude Code にやらせた。ウェブアプリ、ちょいちょいクソコードは書くものの、特にクライアントサイドを書かせるのは割とすんなりやってくれる。

サーバサイドはなんか結構ガイドしないとちゃんとやってくれない。

夜中は消灯

cron で 02:00 に画面を消灯、06:00 に点灯という制御を入れてある。

最近の Raspberry Pi OS (bookworm) は labwc というWayland コンポジタ実装が使われているが、あんまりこれの情報がないのでめんどくさい。

XDG_RUNTIME_DIR=/run/user/1000
 
 # 02:00 にオフ
 0 2 * * * /usr/bin/wlr-randr --output HDMI-A-1 --off
 # 06:00 にオン
 0 6 * * * /usr/bin/wlr-randr --output HDMI-A-1 --on --mode 1920x1080

この手のものはいかに運用を楽にするかというのが問題。「うまく動かない」場合にメンテできるのが自分しかいないからだ。

  1. トップ
  2. tech
  3. おうちサイネージ (食卓カレンダー)

昨今の QNAP には Container Station というコンテナイメージを実行するためのオフィシャルなアプリケーションが提供されている。これを使えば特にQNAP用というわけではないコンテナイメージも自由に動かせて嬉しい。

例えば VOICEVOX のAPIを自宅内で自由に使えたらな~ と思うと、サーバとして常時動いていて、CPU的にも比較的遊びのある QNAP で動かせると嬉しい。HTTP リクエストさえできれば自宅内の Raspberry PI だろうがなんだろうが低スペPCからも音声合成ができる。

https://hub.docker.com/r/voicevox/voicevox_engine

Docker Hub にオフィシャルなイメージがあるので、これを pull する

Create Container で Publish New Port して 50021 を指定する。これで 192.168.5.250:50021 でアクセス可能になる。これだけ

 curl -X 'GET' \
   'http://192.168.0.177:50021/speakers' \
   -H 'accept: application/json'
   
   [
     {
       "name": "四国めたん",
       "speaker_uuid": "7ffcb7ce-00ec-4bdc-82cd-45a8889e43ff",
       "styles": [
         {
           "name": "ノーマル",
           "id": 2,
           "type": "talk"
         },
         {
           "name": "あまあま",
           "id": 0,
           "type": "talk"
         },
         {
           "name": "ツンツン",
           "id": 6,
           "type": "talk"
         },
         {
           "name": "セクシー",
           "id": 4,
           "type": "talk"
         },
         {
           "name": "ささやき",
           "id": 36,
           "type": "talk"
         },
         {
           "name": "ヒソヒソ",
           "id": 37,
           "type": "talk"
         }
       ],
       "version": "0.15.9",
       "supported_features": {
         "permitted_synthesis_morphing": "SELF_ONLY"
       }
     },
     {
         "name": "ずんだもん",
         "speaker_uuid": "388f246b-8c41-4ac1-8e2d-5d79f3ff56d9",
         "styles": [
           {
             "name": "ノーマル",
             "id": 3,
             "type": "talk"
           },
           {
             "name": "あまあま",
             "id": 1,
             "type": "talk"
           },
           {
             "name": "ツンツン",
             "id": 7,
             "type": "talk"
           },
           {
             "name": "セクシー",
             "id": 5,
             "type": "talk"
           },
           {
             "name": "ささやき",
             "id": 22,
             "type": "talk"
           },
           {
             "name": "ヒソヒソ",
             "id": 38,
             "type": "talk"
           },
           {
             "name": "ヘロヘロ",
             "id": 75,
             "type": "talk"
           },
           {
             "name": "なみだめ",
             "id": 76,
             "type": "talk"
           }
         ],
         "version": "0.15.9",
         "supported_features": {
           "permitted_synthesis_morphing": "SELF_ONLY"
         }
       },
  1. トップ
  2. tech
  3. QNAP を VOICEVOX 音声合成サーバにする

こういうのが500円で売っている。なんとなく買ってみた。

設定ツール (MINI KeyBoard.exe) が Google Drive で共有されて、そのQRコードがマニュアルにはってある。どう考えても実行するのは怖いので、まずは解析して設定ツールを自分で書くところからはじめる。

まぁそもそも、信頼できないデバイスをUSBで繋ぐなというのが正論だと思うが……

ILSpyで解析

MINI KeyBoard.exe は .NET アプリのようなので、ILSpy でデコンパイルできる。変なコードはとりあえず含まれていない。たぶん HID Report でなんかしてるだろうから、デコンパイルしたやつを Claude Code に読ませてプロトコルを解析してもらった。といってもそんなに精度高く解析はしてくれないので、結局自分でかなり勘を働かせて指示してやる必要があったけど……

WebHIDで再実装

ソフトは複数のミニキーボードに対応するものっぽいが、自分が持ってるのだけ設定できるツールを WebHID を使って作ってみた。というか Claude Code に作ってもらった。これに関しては完全に指示しかしてないはず。

https://github.com/cho45/webhid-mini-keyboard-configurator

WebHIDのセキュリティ……

Windows の Chrome / Edge で WebHID つかって動くことは確認済みなのだが、残念なことに動かないことがある。Chrome は WebHID に制限をかけていて、汎用キーボードっぽいものとかに対してはレポートの送受信ができないようになっている。この制限にひっかかったりひっかからなかったりする。

開発しはじめて動くまではまったくひっかからなかったのだが、できてからいろいろ試しはじめたらエラーが出たり出なかったりするように。Edge だと動いたり、しばらくたつと動かなかったり。かなりおかしな挙動をする。わけわかんない。

WebHID で作れば変なソフトインストールしないですむやん、というのが嬉しいポイントだけど、こんな挙動だと使えない。こまったねえ

関係ありそうなgithub project

カスタムファームウェア

そもそもファームから変えちゃおうってやつ

  1. トップ
  2. tech
  3. Aliexpress で買ったミニキーボードを WebHID で設定する