RaspberryPiとMESH(ボタンタグ+カスタムタグ)を使ったPCスクリーショット(Mac)の保存操作


経緯

  • 趣味の自転車で使っている室内トレーニングアプリ(zwift,rouvyなど)でトレーニング中にスクリーンショットを保存したい
  • Macではスクリーショットはcommand+shift+3で保存できますが、トレーニング中に操作するのは危険なので、片手でも実行できるようにしたい

作ったもの

MESHのボタンタグを押すとMacのスクリーンショットを指定したフォルダに保存します。

MESHを選択した理由

  • バッテリー内蔵で単体で動作し、自転車に取付け可能で片手で操作できること
  • SDK(JavaScript)があり、RaspberryPiと連携できること

構成

MESH

  • MESHハブアプリケーションでボタンタグをトリガーにHTTP GETを送るカスタムタグのレシピを作成します

カスタムタグとレシピはMESHハブアプリケーションを試してみるを参考にCode:Executeに下記コードを書いて作成します。

var apiURL = "http://ローカルホスト名:ポート番号/";

ajax ({
    url : apiURL,
    type : "get",
    timeout : 1000,
    success : function ( contents ) {
        log("HTTP receive.");
        callbackSuccess( {
            resultType : "continue"
        } );
    },
    error : function ( request, errorMessage ) {
        log("http request error occured");
        callbackSuccess( {
            resultType : "continue"
        } );
    }
});

return {
    resultType : "pause"
};

RaspberryPi

Raspberry Piに「MESH ハブ」アプリをインストールする方法を教えてほしいを参考にMESHハブで作成したレシピの実行環境をラズパイに構築し、HTTPリクエストを受けるローカルサーバー(Node.js + Express + child_process)を起動し、SSH接続(公開鍵認証)でAppleScriptをosascriptコマンドで実行します。

const express = require('express')
const app = express()
const {exec} = require('child_process')

//要ユーザ設定ファイル作成(~/.ssh/config) 
const sshcmd = "ssh [接続するsshのエイリアス名]"
//ファイル形式
const fileformat = "defaults write com.apple.screencapture type [ファイル形式]"
//画像ファイルの保存先パス
const savefilepath ="defaults write com.apple.screencapture location [画像ファイルの保存先パス] "
//AppleScriptのパス
const applescriptpath = "osascript [AppleScriptのパス] "
//AppleScript実行(ファイル形式と保存先指定)
//const cmd = sshcmd + fileformat + "&& " + sshcmd + savefilepath + "&& " + sshcmd + applescriptpath
//AppleScript実行
const cmd = sshcmd + applescriptpath

app.get('/', function (req, res) {
    exec(cmd, (error, stdout, stderr) => {
        if (error) {
            console.error(`exec error: ${error}`);
            return;
        }
        res.status(200)
        res.send('OK')
    })
})

app.listen(3000,() => console.log('Listening on port 3000'));

プログラムはサーバーが落ちても再起動するようにforeverコマンドを指定します。
【Node.js】 RaspberryPiのプログラムを自動起動・永続化・SSH ログアウト後もプロセスを残すを参考にサービス登録、再起動し、Foreverのプロセスを下記コマンドから確認します。

/etc/rc.local
sudo -u pi /usr/local/bin/node /usr/local/bin/forever start -a -d [動かしたいスクリプトのパス]
$ forever list
info:    Forever processes running
data:        uid  command             script                 forever pid  id logfile                    uptime       
data:    [0] EL4C /usr/local/bin/node [動かしたいスクリプトのパス]  1447    1705    /home/pi/.forever/EL4C.log 0:0:0:14.962

SSHはMacから公開鍵を取得し、ユーザ設定ファイル(~/.ssh/config)を作成します。

~/.ssh/config
Host [接続するsshのエイリアス名]
  HostName [ローカルホスト名]
  User [ユーザー名]
  IdentityFile [秘密鍵のパス]
  Port [ボート番号(デフォルトは22)]

Mac

  • スクリーショットを実行するAppleScriptをスクリプトエディタで作成します
AppleScript形式
on fullscreenshot()
    tell application "System Events"
                // Command+Shift+3を入力
        key code 20 using {command down, shift down}
    end tell
end fullscreenshot

fullscreenshot()
JavaScript形式
sys = Application("System Events");
// Command+Shift+3を入力
sys.keyCode("20", { using: ["command down", "shift down"]}); 

スクリプト保存時にファイルフォーマットを「スクリプト」に指定すると「.script」で保存されるため、これをosascriptコマンドで実行します。
なお、AppleScript形式の場合、ファイルフォーマットを「テキスト」に指定すると「.applescript」で保存されますが、それぞれosascriptで実行することが可能です。

最後に

このスクリプトを作成した後、Zwiftではショートカットキーの設定でF10キーでスクリーンショットを指定のフォルダに保存できることを知りました。
ただ、スクリーンショット操作は運動に集中しているとキーでもボタンでも押せないことや押し忘れることがあったため、自動で実行できるような仕組みがあると良いかと思いました。こちらは今後検討予定ですが、映像解析や他デバイスからのトリガーから実行できる仕組みを検討してみようと思います。