以前、作って運用しつつもブラッシュアップ中のRaspberry Pi 3 Model B+とJulius、Open JTalkベースの自作スマートスピーカーがあります。
主な機能は、
尚、ラズパイ用ACアダプタを挿したスイッチ付きコンセントでのON/OFFとは別にラズパイ用boot/reboot/shutdown物理ボタン付き。
音声認識にJuliusを使った自作スマートスピーカーに伝言とメモの機能を実装するにあたり、マイクとスピーカーを専有してしまうOSSやALSAからPulseAudioに移行しました。
ちなみに便利なのでラズパイだけでなく、PC/Debianにも自作スマートスピーカー機能を搭載しています。
自ずとモニタ付きとなるPC版スマートスピーカーには、PC及びラズパイ双方のスマートスピーカー機能のデスクトップアプリとしてPyQt5/Qt Designerによる操作パネルも作成しました。
今回は、Julius・Open JTalk自作スマートスピーカー機能を入れたLinuxパソコンで玄関カメラ、ドアカメラ、庭等々や幼児・児童、要介護者の見守り、ペット見守り、防犯用セキュリティカメラなどにIPカメラ/ネットワークカメラ、USBカメラ/Webカメラ、本格的な防犯カメラなどのビデオカメラの映像を音声操作、スマートスピーカー機能用GUIメニュー操作で表示させてみることにしました。
カメラ機能には、同一ネットワーク内からアクセス可能なIPカメラ/ネットワークカメラとしてラズパイ3B+とUSBカメラを、管理ソフトウェア・アプリとしてZoneMinderを、これの表示にはZoneMinder標準のブラウザを使用しました。
まだ、このカメラの具体的な使途を決めていないので、とりあえず、ライブ映像としておきます。
モニタがあれば、自作ラズパイスマートスピーカーでもできますが、現時点ではヘッドレスなのでモニタのあるPCを対象としました。
と言っても、ブラウザ経由ということでスマホ・タブレットからもカメラ映像を確認できるので、iPhone/iPadなどのiOSやAndroidなどのスマホやタブレットからの音声による操作もできますが、それはともかく、微妙にmDNSが使えないAndroidでもいけるようにラズパイのIPを固定、ホーム画面にブラウザショートカットを作ってあります。
ZoneMinderは、既に使える状態になっており、これは、モニタにブラウザを使い、その時点で同一ネットワーク内からブラウザを介してZoneMinderカメラサーバにアクセスできるようにはなっています。
よって、ここでやることは、音声認識させるためJuliusの辞書にワードを追加、utf-8からeuc-jpへの辞書ファイルエンコード変換、音声合成・応答スクリプトに当該ワードによる条件分岐追加、GUIメニュー操作するために利用中のQtデザイナーでボタンを、PyQtでpythonスクリプトを追記するだけです。
音声操作では、とりあえず、[カメラ表示]、[カメラ表示終了]をキーにChrome互換Chromiumが起動、ZoneMinderサーバによる(カメラ一覧や直接特定の)カメラ映像を表示、応答スクリプト内でpkill chromiumとしてChromiumを終了させるようにしました。
自身は、Firefoxを愛用しているのでブラウザには、pkillで終了させても痛手がない、まず滅多に使うことがないChromiumを使うことにしました。
スマートスピーカー機能用パネル操作では、[カメラ]ボタン押下でChromium+ZoneMinderサーバを起動、用が済んだら、ブラウザChromiumを手動で閉じることに。
アクセス先は、複数のカメラを管理できるZoneMinderならではでIPアドレス/zm、または、hostname.local/zmでカメラ一覧、zmの後にGETパラメータとして[?view=watch&mid=NUM](NUMはカメラに割り当てられる1から始まる数値...のはず)を付与で個々のカメラ映像が表示されます。
ちなみに自身は、とりあえず、後者にしていますが、一覧画面を最初に開く前提でカメラ映像画面からカメラ一覧に戻る機能は、ブラウザ履歴に任せているようで後者から一覧画面に戻るとしたら、ブラウザの入力欄で/zmに編集するしかなさ気です。
よって、この作業、いとも簡単に終えるはずでしたが、意外にも音声操作させる際にブラウザが起動せず、ハマりました。
というか、音声操作でも、あっさりできたのですが、systemdを介したら、一連のスクリプトは全く同じなのに、できないという状況に。
というのも最終的に起動するスクリプト単体やJuliusのモジュールモードと応答スクリプトをそれぞれ端末で起動する分には、音声で操作してもちゃんとブラウザも起動、カメラの映像も表示されるのにsystemdのユニットファイル経由で直接呼ぶスクリプト、そこからJuliusと応答スクリプト起動、更にそこからブラウザを起動するスクリプトを呼ぶ恰好にするとブラウザが起動しなかったのです。
尚、応答スクリプトからブラウザの起動を試みてもダメでした。
結果、unitファイルから起動するJuliusと応答スクリプトの起動を含むシェルスクリプト内に[export DISPLAY=:0.0]を追記することでsystemd経由の音声操作でもブラウザも立ち上がり、無事、解決しました。
ちなみにperlスクリプトである応答スクリプト内でもsystem関数を使ってsystem("export DISPLAY=0.0;any_script");とすればいけましたが、他でもこれが必要となる可能性を考えると上流のスクリプトの方が、妥当でしょう。
そもそも*BSD/Linux/PC-UNIXを日常利用するようになって久しいこともあり、他に影響もなく、GUIが機能しないとなれば、rootとuser間やsshポートフォーワード、VNCなんかでもありがちな環境変数[DISPLAY]だよねという点は、確信をもっていました。
が、systemdのユニットファイルにEnvironmentを使ってDISPLAY=:0.0やDISPLAY=:0、XAUTHORITYなども設定してみましたが、ダメ。
ブラウザを起動できなかったsystemd経由のケースと、これができた端末から応答スクリプトを実行したケースにおける一連のスクリプトファイル時点におけるenv結果の差をdiffやソート後、commコマンドなどで比較、差分すべてをユニットファイルで指定してみるも駄目。
実際にブラウザを起動させている末端のスクリプト、これを呼んでいる応答スクリプト、これを呼んでいるユニットファイルから呼ばれるスクリプトにそれぞれ、DISPLAYを設定してみるもダメ。
ただ、ユニットファイルから呼ばれるスクリプト、そこからJuliusとともに呼ばれる応答スクリプトのenv出力結果が同一であること、前者に単に[DISPLAY=:0.0]としても呼び出す時点だけで前者のenv出力はもちろん、後者以降まで引き継がれるわけがないという、うっかりミスに気づき、前者スクリプトで設定したDISPLAYをexportすることで解決しましたとさ、めでたし、めでたし。