MESHの動きタグとWordPressで1日のアクティビティを(自分だけに)可視化してみた
みなさんこんにちは。今日も結婚できなかったぼくはぼっちなクリスマスイヴを過ごす予定です。この投稿は、 さくらインターネット Advent Calendar 2017 、2017/12/24の投稿です。
ぼっちなクリスマスを過ごす我が家にミニスカサンタなおねぃさんが来るわけがないので、戸締まりはしっかりしておきたいものです。数年前からMESHの動きタグとSwarmのチェックイン履歴をロギングしていますが、WordPressでこれらを可視化してみました。弊社の社屋も兼ねているので、現状大家さんに怒られない範囲においては万全のセキュリティです[ref]MESHだけじゃ心もとないとか言わないでください!エントランスはオートロックなんですから!><[/ref]。
MESHとは
MESHとは、Make(つくって)、Experience(体験して・楽しんで)、SHare(シェアしよう)という意味が込められたIoTデバイスで、難しいプログラミングや電子工作の知識は必要ありません。
[amazonjs asin=”B0154FZFBE” locale=”JP” title=”ソニー Sony MESH 「ボタン」「LED」「動き」 3タグセット MESH-100B3A”]
私はMESH「ボタン」「LED」「動き」3タグセットを購入しましたが、今回使用するのは「動き」タグです。
MESHで実現したこと
MESHの動きタグは玄関のサムターンに両面テープで接着し、解錠と施錠をトリガーにして
- ロギング
- 通知
を実現しています。
また、同じログファイルにSwarmのチェックインをロギングしているので、1日のアクティビティを時系列で追うことができます[ref]チェックイン漏れをSwarmから指摘されて後日チェックインした場合、デイリーでローテーションされたログファイルを編集する必要があります[/ref]。
処理全体のフローを簡単な絵にするとこんな感じになります。
準備するもの
- MESH 動きタグ
- MESHアプリ
- MESHアプリ実行環境
- 私は第4世代のiPadをMESHアプリ実行環境にしています
- Swarmアカウント (チェックイン履歴をロギングする場合)
- Dropboxアカウント (ログファイル保存先 後述するIFTTT連携に必要なため)
- IFTTTアカウント
- Pushbulletアカウント
- Pushbulletのサインアップのため、別途GoogleまたはFacebookのアカウントが必要です
- 自宅サーバーもしくはPC(バッテリー容量のそこそこあるノートPC推奨)
- OS : FedoraもしくはCentOS Ubuntu等、Dropboxとディレクトリが同期できるもの
- bash実行可能なこと
- 私はASUS U24Eシリーズ ウルトラブック 11.6型 HD液晶 U24E-PX2430R レッドを使っています
- USBケーブル+変換コネクタ
- さくらのVPS (私はKUSANAGIを使っていますが、いわゆるLAMP環境にWordPressが乗っており、wp-cliが動けば何でもよいです)
MESHアプリの設定
MESHアプリはこんな感じに設定しています。
MESHアプリではいわゆる if の条件分岐をせず、
- 動きタグの向きが変わったら
- 動きタグ->IFTTTの連携が終わったら
の2つのトリガーで音を鳴らすようにしています。何本もスピーカーに線が伸びているのは、単にギターとベースと歓声を同時に鳴らしたかっただけです。スピーカーを鳴らすことで、万が一誰かが合鍵的なものをこさえて侵入したとしても、侵入者がビビるという心理的効果があるので、サウンドを鳴らすのはおすすめです。
MESH 動きタグの設置
こんな風に、玄関のサムターンに両面テープで固定します。モバイルバッテリーもドアにくっついていますが、これは余剰品活用と充電時のインジケーター点灯で通電確認をするためにくっつけているので、あってもなくてもよいです。電源供給元兼バッチ処理のマシンにUPSがなければ、モバイルバッテリーはあったほうがよいでしょう。
ドアまわりの配線
ドアまわりのUSBケーブルの配線ですが、ドア開閉の際に引っ掛けて断線しないように工夫しています。
画像左側がどこの逸般の誤家庭にもある19インチラックもといシューズボックスですが、観音扉の隙間からUSBケーブルをうまいこと伸ばしています。万が一ケーブルが断線したときに交換しやすいよう、延長ケーブルをかましてMESHにはなるべく配線作業で触れないようにしています。
これが電源供給元兼バッチ処理マシンです。ASUSのU24Eが大好きなので、これにFedora27をインストールしています。
ラックを閉じるとこんな感じで、とてもPCが動いているようには見えません。
MESHの動きタグからDropboxへログを吐く
MESHの動きタグからDropboxのテキストファイルへ開閉ログを吐くために、IFTTT連携を使っています。ここまでは特にプログラミングをする必要はなく、淡々と追記しているだけです。
サムターンの動きは横->縦と縦->横の2種類しかないので、ロギングのアプレットは2つのみです。スクリーンショットにはありませんが、開閉のどちらかでPushbyllet通知を行い、スマホにプッシュ通知するようにしています[ref]かつてはメール通知していましたが、読み返すことはないのでプッシュ通知にしました[/ref]。
通知系の処理は以上ですが、IFTTTからDropboxへのテキスト追記はファイルサイズに制限があるので、デイリーでローテーション(日付変更後に前日のログを切り出して別ファイルに出力)しています。コードはこちらです。CommonLifeLogSorter というシェルスクリプトを書いて、crontabには引数を与えて実行しています。
#!/bin/bash set -eux export LANG=C export LC_ALL=C DATE=$1 # DVC = DAY VALUE CHECK DVC=$(echo ${DATE} | egrep "^[[:digit:]]{4}/[[:digit:]]{1,2}/[[:digit:]]{1,2}$" > /dev/null ; echo $?) [ ! 0 = ${DVC} ] && exit 1 # SDAY = DATE変数の日付をゼロパディングする SDAY=$(echo ${DATE} | date +%Y/%m/%d -f -) BASEDIR=${HOME}/Dropbox LOGDIR=${BASEDIR}/APPS/LOGS LOGNAME=CommonLifeLog LOGFILE=${LOGDIR}/${LOGNAME}.txt OLDLOGDIR=${LOGDIR}/OLD/${LOGNAME}/$(dirname ${SDAY}) OLDLOGFILE=${OLDLOGDIR}/$(echo ${SDAY} | sed -e "s/\//-/g").txt MONTH=$(echo ${DATE} | date +%B -f -) INPUTDAY=$(echo ${DATE} | date +%B" "%d," "%Y -f -) INPUTDAY_NOSUP=$(echo ${INPUTDAY} | sed -e "s/${MONTH} 0/${MONTH} /") # LINES = Lines of VALUE at Logfile counts LINES=$(cat ${LOGFILE} | egrep "(${INPUTDAY}|${INPUTDAY_NOSUP})" | wc -l) # 引数に与えた日付をLOGFILEからgrepしてOLDLOGFILEへ吐き出す関数 LOGGREP() { for USHI in {A,P}M do cat ${LOGFILE} | \ xargs --null | \ egrep ${USHI} | \ egrep "^(${INPUTDAY}|${INPUTDAY_NOSUP}) at" | \ sed -e "s/^${INPUTDAY_NOSUP}/${INPUTDAY}/g" | \ sed -e "s/at 12:/at 00:/g" | \ sort -n done | tee ${OLDLOGFILE} } # 引数に与えた日付をLOGFILEから削除する関数 LOGLINEDELETE() { sed -i "/${INPUTDAY}/d" ${LOGFILE} sed -i "/${INPUTDAY_NOSUP}/d" ${LOGFILE} } mkdir -p ${LOGDIR} ${OLDLOGDIR} if [ ! 0 = ${LINES} -a ! -f ${OLDLOGFILE} ]; then LOGGREP && LOGLINEDELETE fi
05 00 * * * /home/nullpopopo/bin/CommonLifeLogSorter $(date +\%Y/\%m/\%d --date="1 days ago") > /dev/null 2>&1
もう1つ、出かけた後に「そういや鍵閉めたっけ!?」と不安になることはよくあることだと思いますが、鍵開閉の最新ステータスを1つのファイルに抜き出すスクリプト KeyOpenStatusLogger も書きました。
#!/bin/bash #set -eux export LANG=C export LC_ALL=C BASEDIR=${HOME}/Dropbox LOGDIR=${BASEDIR}/APPS/LOGS LOGNAME=CommonLifeLog LOGFILE=${LOGDIR}/${LOGNAME}.txt OLDLOGDIR=${LOGDIR}/OLD/${LOGNAME} OLDLOGFILE=$(find ${OLDLOGDIR}/ -type f | sort -n | tail -n 1) STATDIR=${BASEDIR}/APPS/STATS STATFILE=${STATDIR}/${LOGNAME}-LastStat.txt mkdir -p ${STATDIR} # 検索ワードを変数に格納する ROWORD=ShinagawaNOC-KeyOpenStatus #ROWORD="Retrieval Object" # 異常終了条件判定 # ログファイル、1世代前のファイルの両方がなければ異常終了させる if [ ! -f ${LOGFILE} -a ! -f {OLDLOGFILE} ]; then exit 1 fi # ログファイル、1世代前のファイルの両方に検索ワードがなければ異常終了させる if [ 0 = $(cat ${LOGFILE} ${OLDLOGFILE} | egrep ${ROWORD} | wc -l) ]; then exit 1 fi # ログファイル、1世代前ファイルから最新のステータスを検出する for USHI in AM PM do cat ${OLDLOGFILE} | \ xargs --null | \ egrep ${USHI} | \ egrep ${ROWORD} | \ sed -e "s/$(LANG=C date +%B)[[:space:]][1-9],/$(LANG=C date +%B" "%d,)/g" | \ sort -n | \ tail -n 1 cat ${LOGFILE} | \ xargs --null | \ egrep ${USHI} | \ egrep ${ROWORD} | \ sed -e "s/$(LANG=C date +%B)[[:space:]][1-9],/$(LANG=C date +%B" "%d,)/g" | \ sort -n | \ tail -n 1 done | tail -n 1 | tee ${STATFILE}
こちらは引数なしで毎分cronで実行するようにしています。
以上でロギングの処理はできました。なお、Dropboxですが、設定でLAN Syncを止められる上にファイアウォールで遮断されたことをDropboxが検知したら止まるのですが、不用意に有効になって他ホストへ迷惑をかけないよう、自宅でのみ動かし、自宅のFedoraからさくらのVPSへはrsyncで同期しています。
[blogcard url=”https://www.dropbox.com/ja/help/syncing-uploads/lan-sync-overview”]
WordPress側の設定 (functions.php)
さくらのVPS上にWordPressをインストールして、ここに1日のアクティビティを公開するために、wp-cliを使います。なお、自分のアクティビティは自分だけが見れればよい(外からも自分で確認したいが他人からは見られたくない)ので、functions.php に記述を追加しています。
ログインしていないクライアントが接続しに来たらGoogleへリダイレクトする
function require_login() { if ( ! is_user_logged_in() && ! preg_match( '/^(wp-login\.php|async-upload\.php)/', basename( $_SERVER['REQUEST_URI'] ) ) && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) && ! ( defined( 'DOING_CRON' ) && DOING_CRON ) ) { wp_redirect('https://www.google.com/'); } } add_action( 'init', 'require_login' );
コードは上記の通りで、もしログインしていないクライアントが接続しにきたらGoogleへリダイレクトすることで、コンテンツを保護します。ただし、wp-login.phpとasync-upload.phpは例外としています。また、Ajaxやwp-cronの邪魔もしないように条件分岐しています。こちらは前職の黄色い上司のコードを参考にさせていただきました[ref]なお、ログインURLを変えている場合は、ログイン用のファイルを正規表現で追記してください[/ref]。
[blogcard url=”http://www.warna.info/archives/827/”]
wp-cliで1日のアクティビティを投稿する
wp-cliで1日のアクティビティを投稿する処理を、さくらのVPS側にシェルスクリプトで書いてあげます。ログのソート処理が終わった5分後くらいを目処に、OS側のcronでこれを実行しています。
このシェルスクリプトの実行ユーザーはkusanagiである必要はありませんが、他ユーザーが実行する場合、 /home/kusanagi/<プロファイル名>/DocumentRoot ディレクトリ以下のファイルが読めることが条件です。
以下コードはKUSANAGI環境を前提としているので、非KUSANAGI環境である場合はDocumentRootのPATH(変数WPDIRの値)をよしなに書き換えてください。
もう1点、WP_POST_ACTIVITY 関数中で実行しているwpコマンドの引数「–post_category」に代入する数字ですが、投稿カテゴリーに対応するカテゴリーIDの数字を与えてあげてください。
#!/bin/bash WPUSER=wordpress_username PROFILE=hogehoge.example.com POST_TITLE="$(date +%Y/%m/%d --date="1 days ago") 行動履歴" MESSAGE="昨日の行動履歴はありませんでした。" TODAY=$(date +%Y%m%d) YESTERDAY=$(date +%Y%m%d --date="1 days ago") PROGNAME=$(basename ${0}) TXTFILE_DIR=${HOME}/TXT TXTFILE=${TXTFILE_DIR}/${PROGNAME}_${TODAY}.txt OLD_TXT=${TXTFILE_DIR}/${PROGNAME}_${YESTERDAY}.txt WPDIR=/home/kusanagi/${PROFILE}/DocumentRoot LOGDIR=${HOME}/APPS/LOGS/OLD/CommonLifeLog YESTERDAY_YEAR=$(date +%Y --date="1 days ago") YESTERDAY_MONTH=$(date +%m --date="1 days ago") YESTERDAY_DAY=$(date +%Y-%m-%d --date="1 days ago") LOGFILE=${LOGDIR}/${YESTERDAY_YEAR}/${YESTERDAY_MONTH}/${YESTERDAY_DAY}.txt LOCKFILE=${TXTFILE_DIR}/.${PROGNAME}_${TODAY}.LOCK LOCK_OLD=${TXTFILE_DIR}/.${PROGNAME}_${YESTERDAY}.LOCK MAIN_FUNCTION(){ if [ ! -f ${LOCKFILE} ]; then if [ -f ${LOGFILE} ]; then CREATE_POST_TXT_HISTORY else CREATE_POST_TXT_NOHISTORY fi WP_POST_ACTIVITY touch ${LOCKFILE} rm -f ${LOCK_OLD} fi } WP_POST_ACTIVITY(){ POST_AUTHOR_EXTRACT /usr/local/bin/wp --path=${WPDIR} \ post create ${TXTFILE} \ --post_status=private \ --post_title="${POST_TITLE}" \ --post_category=666 \ --post_author=${POST_AUTHOR} } CREATE_POST_TXT_HISTORY(){ cat << _EOL_ | tee ${TXTFILE} 昨日のアクティビティは以下の通りです。 <pre> $(cat ${LOGFILE} | cut -b 22-) </pre> _EOL_ } CREATE_POST_TXT_NOHISTORY(){ cat << _EOL_ | tee ${TXTFILE} ${MESSAGE} _EOL_ } POST_AUTHOR_EXTRACT(){ POST_AUTHOR=$( /usr/local/bin/wp --path=${WPDIR} \ user list \ --format=json 2> /dev/null | \ sed -e "s/},{/}\n{/g" | \ egrep "(\"user_login\":\"${WPUSER}\")" | \ sed -e "s/[\[{,]/\n/g" | \ egrep ID | \ awk 'BEGIN {FS=":"} {print $NF}' ) } MAIN_FUNCTION
玄関の開閉があったりSwarmのチェックインがあったりしたらアクティビティログファイルの中身が投稿され、もし1日引きこもっていれば、ブログに「昨日の行動履歴はありませんでした。」と投稿されます。
他にも、1分以上ドアが開きっぱなし(解錠ステータス)の場合、テキストファイルのタイムスタンプとログの最終行を判断して「開きっぱなしだよ!」と検知したらスマホに通知するようにもしています。
いかがでしたでしょうか。シェルスクリプトなIoTでQoLの向上をご提案いたします。ではまた!
[amazonjs asin=”B06Y5LM4RQ” locale=”JP” title=”sakura.io さくらの通信モジュール(LTE)”]