Broadlink RM mini3を使ってhomebridgeでRoombaを動かす

Broadlink RM mini3という格安のWifiコントロールの赤外線リモコンを導入。

スマホアプリから直接コントロールすることはないだろうと、並行輸入品を購入すると、なんと、アプリからじゃないとWiFiSSIDの指定すら出来ないという仕様。スマホ以外から初期設定することができないようです。

で、並行輸入品は国内配信されているアプリは使えないようになっています。

なんとかして海外製アプリをAPKから入れてWiFi接続設定をすることに。

参考にしたのはこのサイト

obakasanyo.net

で、Wifiに接続できたらhomebridgeのbroadlink-rmプラグインをインストールして設定していきます。

gentoo # npm install -g homebridge-broadlink-rm

 

まずはルンバのCLEANボタンをリモコンで飛ばしたいと思い、認識させます。

f:id:naoyukinagano:20171209203623j:plain

 

と、思ったら、うんともすんとも言いません。

f:id:naoyukinagano:20171209201908j:plain

裏を見ると、ケータイでおなじみの逓信省マークが!

どうやらRF(無線)タイプのリモコンだったようです。

そこで、一か八か、ヤフオクでルンバの赤外線リモコンを落札しました。それがコチラ

f:id:naoyukinagano:20171209202032j:plain

 

とりあえず、config.json

"platforms": [
{
"platform": "BroadlinkRM",
"name": "Broadlink RM"

}]

だけを設定すると、HomeKitアプリにLearnスイッチが現れるはずです。

f:id:naoyukinagano:20171209202927j:plain

 

このLearnスイッチを押して5秒以内にRM mini 3に向かって学習したいリモコンのボタンを押すと、homebridgeのログにリモコンコードが表示されます。

ルンバのボタンのコードはコチラ

 

CLEANボタン

26005a00622021611f611f615f21206120611f0002a96121206120611f615f21206120611f0002a8622120611f621f615f2120611f61200002a8612220611f6120615f2120611f61200002b06220205f225f215f61212061206020000d050000000000000000000000000000

 

・・・ボタン(500シリーズ以降はDOCKとして機能)

26005a00622021602061206020616020216060000270622021602061206020616020216060000268622021602061206020615f2021615f0002696220216020611f6120615f2021615f0002696220206120611f6120606020216060000d050000000000000000000000000000

 

これをconfig.jsonに登録します。(ONでCLEAN、OFFでDOCKです。)

"platforms": [
{
"platform": "BroadlinkRM",
"name": "Broadlink RM",
"accessories": [
{

"name":"Roomba",
"type":"switch",
"data":{
"on":"26005a00622021611f611f615f21206120611f0002a96121206120611f615f21206120611f0002a8622120611f621f615f2120611f61200002a8612220611f6120615f2120611f61200002b06220205f225f215f61212061206020000d050000000000000000000000000000",
"off":"26005a00622021602061206020616020216060000270622021602061206020616020216060000268622021602061206020615f2021615f0002696220216020611f6120615f2021615f0002696220206120611f6120606020216060000d050000000000000000000000000000"
}
}
]
}
]

インデントがなくなると読みにくいですね。

これで、HomeKitからルンバが動くようになりました。

が、ルンバの赤外線受信感度が悪い・・・。

Homebridgeでマイナスの温度を表示する

Homebridgeで温度を表示するプラグインが多数ありますが、そのうちのいくつかはマイナス表示が出来ません。

これはHomebridgeのデフォルトではマイナスを扱わないから。

プラグインでオーバーライドすることで、マイナスを扱うことが出来ます。

基本的には、

service.getCharacteristic(変数).setProps({minValue: -100});

という感じで、.setProps({minValue: -100})をプロパティにセットすることでマイナスを扱える。

 

homebridge-http-temperature-humidityプラグインの場合、index.jsの98行目あたりに.setProps({minValue: -100, maxValue: 100})を追加すると良い。

temperatureService = new Service.TemperatureSensor(this.name);
temperatureService
.getCharacteristic(Characteristic.CurrentTemperature)
.setProps({minValue: -100, maxValue: 100})
.on('get', this.getState.bind(this));
services.push(temperatureService);

ん? GitHubに上がっている最新版は.setProps({ minValue: -273, maxValue: 200 })が入ってますね。

最新版はこんなことしなくて良さそうです。

 

 homebridge-advanced-http-temperature-humidityプラグインの場合94行目の後ろに追加します。

.getCharacteristic(Characteristic.CurrentTemperature).setProps({minValue: -100, maxValue: 100})

 これで、マイナスも表示されます。

北国の強い味方です。

 

Raspberry pi zero wとhomebridgeとHomeKitの連携その2

先日、Raspberry Pi Zero Wで、リビングのライトのスイッチを、壁のプッシュスイッチからも、iOSのHomeKitからもON/OFFできるように、まずはRaspberry Piに接続したプッシュスイッチでリレーを、とりあえずON/OFF出来るようにしました。

gentoolinux.hatenablog.com

しかし、このときはadd_event_detectをうまく使えず、while:ループの中で細かなsleepでごまかしながら力業で、プルアップされたGPIOインプットがGNDに落ちたかどうかを検出していました。

sleepを挟んでいるとはいえ、かなりCPUリソースを消費していたようです。

今は良いですが、夏は熱でスタックしそう・・・。

f:id:naoyukinagano:20171121223443j:plain

 

で、add_event_detectで立ち上がりエッジを検出後にだけ、該当のGPIOインプットがGNDに落ちているかを判定し、該当のインプットなら処理続行、該当のインプットでなければ処理中断をしてあげれば、問題なく動かせることに気づきました。

そこで、スクリプトの書き直しです。

また、killコマンドで終了したときにもしっかりとGPIOのクリーンナップが出来るように、シグナルハンドラも入れてみました。

こんな感じになりました。

#! /usr/bin/python

 

#必要なモジュールをインポートします。requestsはその1の記事を参照してインストールしてください。

import signal
import sys
import RPi.GPIO as GPIO
import time
import requests

 

#スイッチのGPIOとリレーのGPIOを指定します。

switch = 23
relay = 26

 

#リレーはwebiopiのほうからコントロールします。

#なぜならRPiライブラリでGPIO.setupすると、webiopiからはコントロール出来なくなるからです。

#リレーのURLを生成するための変数です。

url_front = 'http://localhost:8000/GPIO/'
url_back = '/value'
url_function_out = '/function/out'
url = url_front + str(relay) + url_back
url_function = url_front + str(relay) + url_function_out
url0 = url + '/0'
url1 = url + '/1'

 

#スイッチにつながったGPIOをプルアップされたインプットとして設定します。

GPIO.setmode(GPIO.BCM)
GPIO.setup(switch, GPIO.IN, pull_up_down=GPIO.PUD_UP)

 

#webiopiからリレーに接続されているGPIOをアウトプットとして設定します。

#設定にはPOSTメソッドを使います。
gpio_function = requests.post(url_function)

 

#エッジ検出後の動作をコールバック関数として記述します。

def switch_callback(switch):

    if GPIO.input(switch)==0:    #スイッチのGPIOがGNDに落ちているかどうかを判定。落ちていたら次の処理へ
         status = requests.get(url)    #リレーのGPIOの状態をwebiopi経由で取得するためにurlをgetで叩く
         if status.text=='1':    #getでリクエストしたBODY部分は.testに入るので、その値が(文字の)1かどうか判定
             requests.post(url0)    #1ならwebiopi経由で0にするためにPOST
             time.sleep(0.5)    #誤動作防止のため0.5秒スリープ
         else:
             requests.post(url1)    #getでリクエストしたBODYが1以外(つまり文字の0)だったら、webiopi経由で1に
             time.sleep(0.5)    #誤動作防止のため0.5秒スリープ

 

#killを受け取ったときの動作を関数として記述

def handler(signal, frame):   
     GPIO.cleanup()    #設定したGPIOを解放(解放しないと次に使えない)
     print "switch.py terminated for GPIO",switch
     sys.exit(0)    #終了

 

#スイッチとなるGPIOのエッジ(電位差の立ち上がりか立ち下がり)を検出したらswitch_callback関数を呼び出す。

GPIO.add_event_detect(switch, GPIO.RISING, callback=switch_callback, bouncetime=300)

#killされたらhandler関数を呼び出す。

signal.signal(signal.SIGTERM, handler)

 

#エラーが検出されても続行させる。エラー検出後の動作はexceptに記載

try:

     while True:    #永遠とループ
     time.sleep(0.1)    #イベントかエラーが出るまで0.1秒スリープの繰り返し

 

#ctrl+Cで中止が発生した場合の処理

except KeyboardInterrupt:
     print '\nswitch.py Stopped'
     GPIO.cleanup()    #設定したGPIOを解放(解放しないと次に使えない

 できるだけ細かくコメントを書いてみました。

実際にはコメントははずしています。

30分で0.5℃、CPU温度が下がりました。誤差かな?

 

さて、homebridgeの記載をwebiopiに合うように直します。

config.jsonの該当部分のみを出してみましょう。

{
"accessory": "Http",
"name": "Spot and Bracket Light",
"switchHandling": "realtime",
"http_method": "POST",
"on_url": "http://192.168.x.y:8000/GPIO/26/value/0",
"off_url": "http://192.168.x.y:8000/GPIO/26/value/1",
"status_url": "http://192.168.x.y:8000/GPIO/26/value",
"status_on": "0",
"status_off": "1",
"service": "Switch",
"brightnessHandling": "no",
"brightness_url": "",
"brightnesslvl_url": "",
"sendimmediately": "",
"username" : "",
"password" : ""
},

IPアドレスのx.yはご自身の環境に合わせて読み替えてください。

http_methodですが、EspEasyの場合はgetメソッドでコントロールしていたのに対し、webiopiはPOSTメソッドでコントロールします。

URLの中にある26は、対象のリレーが接続されているGPIOです。

サインスマートのリレーは0でON、1でOFFなので、"status_on": "0","status_off": "1"を明確に定義しています。

ServiceはLightにした方がよかったかも・・・。

 

これでhomebridgeを再起動すると、壁スイッチからもHomekitからも使えるようになりました。

しかも、EspEasyと違い、webiopiの動作が速いため、同時に複数のリクエストを捌くことができるようで、HomeKitのシーンで、3つのスイッチを同時に消灯することが可能になりました。

Raspberry pi zero wとhomebridgeとHomeKitの連携その1

だいぶ以前にnodeMcuで4chリレーモジュールのうち、3chを動かし、かつ、物理スイッチからも動かせるようにしました。

gentoolinux.hatenablog.com

しかし、1ヶ月後、WiFiアクセスポイントを増やしたことからなにやら不安定に。

再起動を繰り返す始末。

別なモジュールを買っても同じ。

ESP8266は電源周りがだいぶシビアだそうでして、その影響なのか、exception(29)を吐いてstuckします。

3.3Vのピンに470μFの電解コンデンサをつけたけど、解消されず。

 

そこで、Raspberry Pi zero wに入れ替えた顛末を。

f:id:naoyukinagano:20171121223443j:plain

 

Raspberry piにRaspbian Strechを入れてSSHするところまではいろいろなところに乗っているので割愛。

rootでRaspbian Strechが動かせることを前提に話を進めます。

 

まずは、RaspbianにWebiopiをインストールします。

参考にしたのはこのページ

nw-electric.way-nifty.com

あ、ただ、Python3の有効化をし忘れました(^^ゞ

root@raspberrypi: ~/# mkdir webiopi

root@raspberrypi: ~/# cd webiopi

root@raspberrypi: ~/webiopi# wget https://sourceforge.net/projects/webiopi/files/WebIOPi-0.7.1.tar.gz

root@raspberrypi: ~/webiopi# tar xvzf WebIOPi-0.7.1.tar.gz

root@raspberrypi: ~/webiopi# cd WebIOPi-0.7.1

root@raspberrypi: ~/webiopi/WebIOPi-0.7.1# wget https://raw.githubusercontent.com/doublebind/raspi/master/webiopi-pi2bplus.patch

root@raspberrypi: ~/webiopi/WebIOPi-0.7.1#./setup.sh

んで、Basic認証をハズしました。

root@raspberrypi: ~/# nano /etc/webiopi/config

 /etc/webiopi/configファイルの以下の2箇所をコメントアウト

#passwd-file = /etc/webiopi/passwd

#prompt = "WebIOPi"

で、サービス有効化とスタートです。

root@raspberrypi: ~/# update-rc.d webiopi defaults

root@raspberrypi: ~/#/etc/init.d/webiopi start

 

続いて、PythonでGPIOがGNDに落ちるとトグルスイッチになる(消えてたら付く、付いていたら消える)ようなスクリプトを書きます。

本当ならadd_event_detectを使いたいところなのですが、電位差の検出がシビアすぎて、隣のスイッチまで検出されてコールバックが実行されてしまう始末。

ここはCPUリソースを喰ってでも安定した動作を目指しました。

追記:add_event_detectでは確かに微妙なエッジも検出してしまいますが、検出後のコールバックに"本当に該当スイッチのGPIOが0なのか? を判定するif文を入れれば誤動作を抑止できることがわかりました。詳しくは「その2」へ

 

webiopiからGPIOのピン状況を取得したり、書き込んだりするために、Pythonのrequestsライブラリを利用します。

 root@raspberrypi: ~/# apt-get install python-requests

そして、Rpi.GPIOライブラリ(プリインストール)を活用してスイッチのGPIOがGNDに落ちるのを読み取って、リレーのGPIOの状況を取得し、その状況に応じてGPIOのValueを変化させます。

何度も誤動作しないよう、1度動作したら1秒スリープさせます。

スイッチの読み取りも0.1秒ごとにしました。

プッシュスイッチがGPIO16、リレーがGPIO19で、サインスマートのリレーなので0でON、1でOFFです。

 

追記:これはとても迅速に検出可能ですが、CPUリソースを喰いますのでおすすめしません。

/root/switch/switch1.py

#! /usr/bin/python

import RPi.GPIO as GPIO
import time
import requests
switch = 16
relay = 19
url_front = 'http://localhost:8000/GPIO/'
url_back = '/value'
url_function_out = '/function/out'
url = url_front + str(relay) + url_back
url_function = url_front + str(relay) + url_function_out
url0 = url + '/0'
url1 = url + '/1'

GPIO.setmode(GPIO.BCM)
GPIO.setup(switch, GPIO.IN, pull_up_down=GPIO.PUD_UP)
gpio_function = requests.post(url_function)

try:
   while True:
   if GPIO.input(switch)==0:
     status = requests.get(url)
     if status.text=='1':
       requests.post(url0)
       time.sleep(1)
     else:
       requests.post(url1)
       time.sleep(1)
   time.sleep(0.1)

except KeyboardInterrupt:
   print '\nswitch.py Stopped'
   GPIO.cleanup()

これらのswitch変数とrelay変数の違いで3ch分をswitch1.py,switch2.py,switch3.pyとして作成しました。

起動時に実行するように、/etc/rc.localに記述します。

# switch read

/root/switch/switch1.py &
/root/switch/switch2.py &
/root/switch/switch3.py &

exit 0

これでスイッチとリレーが連動するようになり、リビングのライトが通常通り使えるようになりました。

次は、homebrigeのconfigを書き換えます。

 

Node.jsのバージョンコンフリクト

Node.jsのバージョンアップなどを行ったあっと、homebridgeを起動すると、次のようなエラーが出て起動できなくなった。

 

 /usr/lib64/node_modules/homebridge/node_modules/mdns/lib/dns_sd.js:35
throw ex;
^

Error: The module '/usr/lib64/node_modules/homebridge/node_modules/mdns/build/Release/dns_sd_bindings.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 48. This version of Node.js requires
NODE_MODULE_VERSION 57. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).
at Object.Module._extensions..node (module.js:664:18)
at Module.load (module.js:554:32)
at tryModuleLoad (module.js:497:12)
at Function.Module._load (module.js:489:3)
at Module.require (module.js:579:17)
at require (internal/module.js:11:18)
at Object.<anonymous> (/usr/lib64/node_modules/homebridge/node_modules/mdns/lib/dns_sd.js:24:20)
at Module._compile (module.js:635:30)
at Object.Module._extensions..js (module.js:646:10)
at Module.load (module.js:554:32)

 

どうも、Node.jsのバージョンが、現環境と違うものでコンパイルされているよ。リビルドし直すかインストールし直してね。

と、言っているようです。

インストールし直してもダメだったので、リビルドするのですが、どうやらリビルドコマンドを実行するディレクトリがちゃんとした場所でないと動いてくれないようです。

上記のようにhomebridgeをリビルドするなら、

 

gentoo # cd  /usr/lib64/node_modules/homebridge/

gentoo /usr/lib64/node_modules/homebridge # npm rebuild

 

というように、該当のNode.jsのモジュールがインストールされているディレクトリに移動してからnpm rebuildを実行しましょう。

Let's EncryptでメールサーバーをSSL/TLS化するときの注意

SSL証明書はいままで3~4年更新の安いものを導入していました。

私が管理する義父の会社のサーバーもそうでした。

が、今は無料でSSL化できる良い時代がやってきました。

letsencrypt.jp

未だに80や443以外のポートを使っているときにWebrootでACMEによる実在確認が出来ないという不可解があるのですが(何が悪さしているのか、ログが出てこないので切り分けが出来ない・・・)、普通に80や443のWebrootで実在確認できれば、その後もちゃんと3ヶ月の有効期限が到来する前に、cronでrenewすれば更新されます。

 

で、メールも暗号化していると、Let's Encryptはrenewで更新されているし、Apache+ブラウザもなんともないのに、メールソフトだけは「証明書の有効性が確認できませんでした」という警告が出て、メールの送受信が出来なくなります。(あ、受信しか試してなかったかも)

 

私はPostfix + Dovecotで運用しているのですが、どうもPostfixやDovectは起動時だけ証明書を読むようで(Dovecotだけだったらゴメン)、起動後は証明書の読み込みは一切行わず、起動時に読み込んだ証明書を粛々とクライアントに渡しているようで、Let's Encryptが証明書ファイルを更新しても、反映されないようです。

 

そしてローレベルな解決方法。

更新は週に1度、/etc/cron.weekly配下にリニューアルコマンドのBASHスクリプトを置いていますので、そこにPostfix + Dovecotの再起動を指示しました。

 

/etc/cron.weekly

#!/bin/sh

/usr/bin/certbot renew
/etc/init.d/dovecot restart
/etc/init.d/postfix restart 

 

これで、強制的に反映されるでしょう。

再起動しなくて良い、もっとスマートな解決方法ないかしら?(証明書を買うという方法はナシで)

一眼レフで撮った写真をGoogle Photosに自動アップロード その2 サーバースクリプト設置

その1では、PQI AirCardを自宅のWiFiで使うための設定を行いました。

f:id:naoyukinagano:20171031211815j:plain

 

今回は、サーバー側の設定をしていきます。

サーバーで実現することは、

PQI AirCardをIPアドレスで死活監視。

・活性後、PQI AirCard内の画像のうち、JPGだけをFTPで取得。その後に重複取得しないようにJPEGファイル名称をとあるファイルにアペンド(追加)

Google APIの認証情報(CLIENT ID,CLIENT SECRET,REFRESH TOKEN)に基づき、ACCESSS TOKENを取得します。

Google PhotosのアルバムIDに対してACCESS TOKENで認証し、JPEGをアップロードします。

・アップロードが終わったら、撮影日ごとにフォルダを作成してファイルを移動。

これをずーっとループでやっていきます。

 

まずは、Google APIの認証情報を取得します。Googleアカウント(Gmailアドレス)があることが前提です。

こちらに書いてあることをそのままやることで、Google Photos用の認証情報が全て得られます。

そう、パクりです。

qiita.com

デベロッパーコンソールにアクセスします。

https://console.developers.google.com

私は既に一度アクセスしているので、こんな画面になります。

f:id:naoyukinagano:20171031233327p:plain

おそらく、先に適当なプロジェクト名を指定しなければならないと思います。

私の場合はGooglePhotosAPIとつけていまして、上部に表示されています。

左側の「認証情報」をクリックします。

f:id:naoyukinagano:20171031233917p:plain

大きく青く「認証情報を作成」と出ていますね。ここから「クライアントIDの作成」を選びます。

f:id:naoyukinagano:20171031234335p:plain

アプリケーションの種類は「その他」を選び、適当な名前をつけます。

私の場合は「GooglePhotosUploader」とつけてみました。

「作成」を押します。

f:id:naoyukinagano:20171031235224p:plain

このように、クライアントIDとクライアントシークレットが取得できました。これをどこかにコピーしておきます。

(Googleデベロッパーコンソールサイト上でももう一度確認できます。)

 

続いては、Webサーバー上のでの作業です。

これこそ、先のtamanobiさんの記事の通りに実行します。

まずは、クライアントIDとクライアントシークレットを使ってオーソリゼーションコードを取得します。

私はGentoo Linuxをサーバに使っている変な人なので、Gentoo上で作業します。(といってもほとんどのLinuxディストリビューションで変わらない作業だと思いますが)

作業ディレクトリとして、/home/homephotoとしました。

ここにget_authorization_code.shという名前でtamanobiさんのスクリプトを作成します。

# mkdir /home/homephoto

# cd /home/homephoto

# nano get_authorization_code.sh

すみません。なんの芸もなく、tamanobiさんのスクリプトを引用させていただきます。

下記のYOUR_CLIENT_IDは、上記で取得したクライアントIDに置き換えてください。

CLIENT_ID="YOUR_CLIENT_ID"
REDIRECT_URI="urn:ietf:wg:oauth:2.0:oob"
SCOPE="https://picasaweb.google.com/data/"
echo "https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=$CLIENT_ID redirect_uri=$REDIRECT_URI&scope=$SCOPE&access_type=offline"

これは、認証ページにアクセスするためのURLを生成するスクリプトです。

実行権限を与えて、実行します。

# chmod +x get_authorization_code.sh

# ./get_authorization_code.sh

こんなURLが出てきます。

はい、このYOUR_CLIENT_IDの部分を書き換えてブラウザでアクセスしても同じことです。はい。

https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=$CLIENT_ID redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://picasaweb.google.com/data/&access_type=offline

 

こんな画面が出てきますので、許可しましょう

f:id:naoyukinagano:20171104215550p:plain

 許可を押すと、味気なくオーソリゼーションコードが表示されますので、どこかにコピペしましょう。

f:id:naoyukinagano:20171104215918p:plain

 

続いて、リフレッシュトークンとアクセストークンを取得します。

この2つのトークンのうち、リフレッシュトークンは明示的に変更をしない限り変わりませんが、アクセストークンの有効期限が3600秒です。

またまたtamanobiさんの記事をパクります。

スクリプトを作りましょう。

# nano get_access_token_and_refresh_token.sh

スクリプトの中身は次の通りですが、YOUR_CLIENT_ID、YOUR_CLIENT_SECRET、先ほど取得したオーソリゼーションコードをYOUR_CODEに入れ替えましょう。

CLIENT_ID="YOUR_CLIENT_ID" # クライアントID
CLIENT_SECRET="YOUR_CLIENT_SECRET" # クライアントシークレット
AUTHORIZATION_CODE="YOUR_CODE" # 認可コード
REDIRECT_URI="urn:ietf:wg:oauth:2.0:oob" # HTTPサーバを起動しなくてもAuthorization_Codeを取得できる

curl --data "code=${AUTHORIZATION_CODE}" --data "client_id=${CLIENT_ID}" --data "client_secret=${CLIENT_SECRET}" --data "redirect_uri=${REDIRECT_URI}" --data "grant_type=authorization_code" --data "access_type=offline" https://www.googleapis.com/oauth2/v4/token

で、実行権限を付与して実行します。

# chmod +x get_access_token_and_refresh_token.sh

# ./get_access_token_and_refresh_token.sh

そうするとJSON形式でリフレッシュトークンとアクセストークンが取得できます。

このリフレッシュトークンが大事になります。

 

そして、Google Photosの特定のアルバムにアップロードしたいので、アルバムIDを調べましょう。

ブラウザで次のURLにアクセスします。

USER_IDの部分はGoogleのユーザーIDに書き換えてください。ユーザーIDはGmailアドレスの@より前ですね。

https://picasaweb.google.com/data/feed/api/user/USER_ID

そうすると、アルバムのリストが現れます。

f:id:naoyukinagano:20171104222659p:plain

アルバムのリンクURLを見ると、次のような法則になっています。

https://picasaweb.google.com/"Picasaのユーザー識別"/"アルバムID"?locked=true

このアルバムIDを控えておきます。

 

さて、ここからはオリジナルだったりtamanobiさんのスクリプトの改変だったりです。

 

まず、PQI AirCardの死活監視を行い、アクセスできたらFTPでJPGだけをGETします。

GETし終わったら、同じファイルをGETしないよう、GETしたファイルリストを作ります。

アクセストークンを取得し、JPEGファイルをGoogle Photosにアップロードします。

その後、exiftoolというアプリケーションを使って、年月日(YYYYMMDD)のディレクトリに分けて保存します。

というのを繰り返します。

お気づきの方もいらっしゃると思いますが、デジカメでファイルを連番で記録するようにしていると、XXX_9999.JPGの次は新しくディレクトリを作成し、たとえばDCIM/200CANONの次はDCIM/201CANONに保存されます。

もし10000枚目だとしたらあきらめてください((^^ゞ)

ちなみに、DCIM/200CANONディレクトリの中身を全て消去し、DCIM/201CANONのディレクトリ自体を消去すると、次からデジカメはDCIM/200CANONに撮影写真を保存していきます。

あと、なぜDCIM200CANONなのかというと、PQI AirCardにはDCIM/199WIFIという制御用ディレクトリがあるため、200CANONにしないと199WIFIに保存してしまいます。制御用のJPEGもダウンロードしてしまうことになるためです。

 

getophoto.shという名前のスクリプトにしました。

#/bin/bash

PQI_AIR_IP="192.168.xxx.xxx"

PQI_DIR="sd/DCIM/200CANON"

USER_ID="USER_ID"

CLIENT_ID="YOUR_CLIENT_ID"

CLIENT_SECRET="YOUR_CLIENT_SECRET"
REFRESH_TOKEN="YOUR_REFRESH_TOKEN"

ALBUM_ID="YOUR_ALBUM_ID"

 

#自身のディレクトリに移動
cd `dirname $0`

#プロセス番号をpid.txtに保存

echo $$ > pid.txt

 

#ここからループ

while :

do

 

#PQI AirCarにhttp出来るかどうか試す
ALIVE=$(/usr/bin/wget -nv --spider --timeout 60 -t 1 http://${PQI_AIR_IP}/ 2>&1 | grep -c '200 OK')

 

#PQI AirCadが生きていた場合の処理

if [ $ALIVE -eq 1 ]; then

 

#過去にGETしたJPGファイルのリストdonelist.txtを読み込む。念のため改行を削る

DONELIST=`cat donelist.txt | sed -e ':loop; N; $!b loop; s/\n//g'`

 

#FTPでJPGをGET
/usr/bin/wget -m -q -N --reject=${DONELIST} --no-host-directories --no-directories -A .JPG ftp://${PQI_AIR_IP}/${PQI_DIR}

 

#GETしたJPGを改行などを削除してdonelistに保存

JPGLIST=`/bin/ls | grep .JPG`
if [ -n "$JPGLIST" ]; then
/bin/echo "," >> donelist.txt
/bin/ls | grep .JPG | sed -e ':loop; N; $!b loop; s/\n/,/g' >> donelist.txt
sed -i -e ':loop; N; $!b loop; s/\n//g' donelist.txt
fi

 

#アクセストークン取得
curl -s --data "refresh_token=${REFRESH_TOKEN}" --data "client_id=${CLIENT_ID}" --data "client_secret=${CLIENT_SECRET }" --data "grant_type=refresh_token" https://www.googleapis.com/oauth2/v4/token > token.JSON
ACCESS_TOKEN=`cat token.JSON | jq -r '.access_token'`


ENDPOINT="https://picasaweb.google.com/data/feed/api/user/${USER_ID}/albumid/${ALBUM_ID}?access_token=${ACCESS_TOKEN} "

 

DIR="./" # FTPでGETしたJPGファイルが保存してあるディレクトリを指定

IFS_BAK=${IFS}
IFS="
"

 

#ファイル数だけループ
FILES=`ls -1 ${DIR}`
for FILE in ${FILES}
do

 

#MIMEタイプの指定
if [ "${FILE}" != "" ]; then
TYPE="UNKNOWN"
case "${FILE}" in
*\.bmp) TYPE="image/bmp";;
*\.JPG | *\.jpg) TYPE="image/jpeg";;
*\.png) TYPE="image/png";;
*) TYPE="UNKNOWN";;
esac
# echo "$TYPE $FILE $LENGTH"
if [ "${TYPE}" = "UNKNOWN" ]; then
continue;
fi
LENGTH=`ls -l "${DIR}/${FILE}" | tail -n1 | sed -E 's/ +/ /g' | cut -d' ' -f5`

 

#ファイルをアップロード

curl -s -XPOST "${ENDPOINT}" \
-H "Content-Type:${TYPE}" \
-H "Content-Length:${LENGTH}" \
-H "Slug:${FILE}" \
--data-binary "@${DIR}/${FILE}" > /dev/null
sleep 1
fi
done
IFS=$IFS_BAK

 

#ファイルのアップロードが全て終わったら、撮影日でYYYYMMDDというディレクトリを作ってファイルを移動
/usr/bin/exiftool -q '-Directory < CreateDate' -d %Y%m%d *.JPG
fi

sleep 10
done

 

これも実行権限をつけて実行しましょう。

chmod +x getophoto.sh

./getophoto.sh &

 

これで、サーバーと同じネットワークセグメントにいる限り、つまり、室内では、撮影する度にGoogle Photosの指定したアルバムにアップロードされます。

たしかGoogle Photosの仕様で、1アルバム1000枚までしか保存できないはずですので、上限に達したら新しいアルバムに変えましょう。