Android向けにHome Assistant on Gentoo

いままでiOS向けにHomebridgeの環境を整えていました。
が、iOSのHomeKit以外にHomebrigeとやりとりするようなアプリケーションがありません。
私のメインスマホはGalaxy S7 Edge。バリバリのAndroidです。
なので、家のIot機器(といっても、ほとんど電灯スイッチ)をAndroidからも扱えるようにしたい。

Linuxサーバー上動くホームオートメーションサーバーアプリケーションは、Domoticz、OpenHABなどがあるようですが、ここはGentooで簡単に動きそうなHome Assistantにしました。
「日本語がない」というのが欠点ですが、どうせそんなに難しい英語は使いません。
LivingとかLightとかですよ。中学生英語です。

そして、Home AssistantはAndroidアプリがあるのもイイ!!

LaymanにeBuildを置いてくれているツワモノもいるようですが、ここは公式に則って手動でインストールします。
と、言っても、環境を汚さないように/home配下に専用のホームディレクトリを作って、専用のユーザー権限で動かします。

やりかたは、ほぼこの公式ページです。
home-assistant.io

ただ、この公式ページは「初心者向け」って書いてあるのに、あんまり初心者向けじゃなさげ。

まずは、ユーザーとディレクトリを作成します。

gentoo ~ # useradd -m /home/homeassistant -G homeassistant homeassistant

(あるていど設定が終わってから、シェルの設定を/bin/falseにします。)

suでhomeassistatになります。

gentoo ~ # su homeassistant
gentoo ~ $

で、/home/homeassistantディレクトリで、公式ページに書かれているインストール作業を実施します。
まずは、Python3の仮想環境を構築します。ってもたったの1行。

gentoo ~ $ python3 -m venv /home/homeassistant

そしてその環境に入ります。といっても、ソースコマンドで環境変数が変わるだけかもしれませんが。

gentoo ~ $ cd /home/homeassistant
gentoo ~ $ source bin/activate

そして、homeassistantのインストールです。

gentoo ~ $ python3 -m pip install homeassistant

ここまで来れば、仮想環境を抜け出してかまいません。
というか、ルートにもどれば良いだけだと思います。

で、公式サイトに書いてある自動起動スクリプトは、Gentooには通用しないので、Gentooのお作法で、自分でインストールしたアプリケーションは/etc/local.d/の中に入れます。

/etc/local.d/homeassistant.start

#!/bin/sh
sudo -u homeassistant -H /home/homeassistant/bin/hass --pid-file /home/homeassistant/hass.pid  --daemon > /var/log/home-assistant.log 2>&1

/etc/local.d/homeassistant.stop

#!/bin/sh
kill $(cat "/home/homeassistant/hass.pid") 2>&1

これで(自動)起動する準備は整いました。
でも、起動しても、うまくいって日の出日の入りとか、chromecastやUpnp(DLNA)デバイスが見つかる程度だと思います。
(それでも、自動でみつかるだけマシですが。)

/etc/init.d/localを再起動すると、起動するはずです。

gentoo ~ # /etc/init.d/local restart

で、ブラウザ(Firefox等のmozilla系がいいです。)で、8123番ポートにアクセスします。
http://サーバーのip:8123/


f:id:naoyukinagano:20180121234519p:plain
すみません。写真は設定し終わった後なので、いろんな電灯スイッチや情報が入っています。
このあたりの設定は次回に。

FlashAir(W-04)の画像をGoogle Photos(Picasa Web)のアルバムにアップロードするスクリプトを改善

前回、FlashAir(W-04)の画像をGoogle Photos(Picasa Web)のアルバムにアップロードするスクリプトを作りました。

gentoolinux.hatenablog.com

で、テストして成功したと喜んでおりましたが、なんだか遅い。
特に、猫写真を撮る際は連写をするため、何枚もアップロードしなければなりませんが、遅すぎる。

原因は簡単。
FlashAirから画像リストを取得、ダウンロード済みかどうか確認し、1枚ダウンロード、PicasaWebにアクセスするためのトークンをゲット、ようやくアップロード、日付ディレクトリに移動、最初に戻る。
というのをいちいち繰り返していたため、遅いのです。

もちろんアップロード中にカメラの電源を消すことも出来ません。

で、FlashAirからの画像ダウンロードを全て行ってから、PicasaWebにアップロードするようにアルゴリズムを変更します。
アルゴリズムといっても、単にスクリプトの行を入れ替えただけ。

#!/bin/bash

#初期変数一覧

#Flashairのアドレス
FLASHAIR_IP="http://192.168.x.161"

#DCIMフォルダの名称
DCIM_FOLDER="CANON"


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

#プロセスIDを保存
echo $$ > pid_flashair.txt

#DCIMフォルダのURLを生成
WGET_PRE_URL=${FLASHAIR_IP}"/DCIM/"



while :
do

#Flashairを死活監視
ALIVE=$(/usr/bin/wget -nv --spider --timeout 60 -t 1 ${FLASHAIR_IP} 2>&1 | grep -c '200 OK')

        #もし死活監視で生きていたら
        if [ $ALIVE -eq 1 ]; then

                #配列を初期化
                echo "Flashair - Format Arrys."
                DCIM_LIST=()
                JPG_LIST=()
                FLASHAIR_DONE=()

                #DCIM配下のディレクトリを配列に代入
                echo "Flashair - get under DCIM directory list."
                DCIM_LIST=(`/usr/bin/wget ${WGET_PRE_URL} -q -O - | grep 'wlansd.push({"r_uri"' | sed -e 's/wlansd.push({"r_uri":"//' -e 's/", "fname":"/\//' -e 's/", "fsize".*//' | grep ${DCIM_FOLDER}`)

                #フォルダの数だけループ処理を実施
                echo "Flashair - Starting loop under DCIM directory."
                DCIM_LOOP=${#DCIM_LIST[@]}
                DCIM_LOOP=$((DCIM_LOOP - 1))
                while [ $DCIM_LOOP -ge 0 ];
                do

                        #フォルダの中のファイルを配列に代入
                        echo "Flashair - Get file list under "${DCIM_LIST[DCIM_LOOP]}
                        JPG_LIST=(`/usr/bin/wget ${FLASHAIR_IP}${DCIM_LIST[${DCIM_LOOP}]} -q -O - | grep 'wlansd.push({"r_uri"' | sed -e 's/wlansd.push({"r_uri":"//' -e 's/", "fname":"/\//' -e 's/", "fsize".*//'`)

                        #ファイルの数だけループ
                        echo "Flashair - Starting loop under "${DCIM_LIST[DCIM_LOOP]}
                        JPG_LOOP=${#JPG_LIST[@]}
                        JPG_LOOP=$((JPG_LOOP - 1))
                        while [ $JPG_LOOP -ge 0 ];
                        do

                        #アップ済みのファイルかどうかをチェック
                        echo "Flashair - Check FLASHAIR_DONE "${JPG_LIST[JPG_LOOP]}
                        if ! `cat flashair_done.txt | grep -q "${JPG_LIST[JPG_LOOP]}"` ; then

                        #アップ済みになかった場合の処理
                        #wgetによるファイルの取得
                        echo "Flashair - Get file "${JPG_LIST[JPG_LOOP]}
                        /usr/bin/wget -q -N --no-host-directories --no-directories ${FLASHAIR_IP}${JPG_LIST[JPG_LOOP]}

                        #flashair_done.txtにファイル名を追加
                        echo "Flashair - Output to flashair_done.txt "${JPG_LIST[JPG_LOOP]}
                        echo ${JPG_LIST[JPG_LOOP]} >> flashair_done.txt

                        fi

                        JPG_LOOP=$((JPG_LOOP - 1))
                        done


                DCIM_LOOP=$((DCIM_LOOP - 1))

                #ダウンロードした全てのJPGを2048pxにリサイズ
                echo "Flashair - Resize Images"
                /usr/bin/mogrify -resize 2048x2048 -quality 100 *.JPG

                #ディレクトリ上にあるJPGファイルをpicasaweb APIでアップロード
                echo "Flashair - Uploading Images"
                ./upload_images_in_dir.sh

                #ディレクトリ上にあるJPGファイルを日付ディレクトリで整理
                echo "Flashair - Move Images to YMD directory "
                /usr/bin/exiftool -q '-Directory < CreateDate' -d %Y%m%d *.JPG

                done

        fi

        sleep 10
done

そう、リサイズ、アップロード、ディレクトリ整理を一つだけループの外に置いただけ。
これだけでだいぶ早くなりましたし、アップロードが始まったのを見て、カメラの電源も切ることが出来るようになりました。

FlashAir(W-04)の画像をGoogle Photos(Picasa Web)のアルバムにアップロードする

カメラをEOS7D Mark IIに新しくしたら、SDカードがUHS-I対応と言うことで、Wi-Fi SDカードもUHS-I対応のFlashAir W-04に変更しました。

f:id:naoyukinagano:20180102102210j:plain

そこで、luaスクリプトを使ってFlashAirから直接Google Photosにアップロードを試みたのですが、何度やってもエラー。どうやらPicasaweb APISSL証明書のサイズが大きいらしく、ネゴシエーションエラーを起こすようです。

 

seesaawiki.jp

そこで、方向を変え、PQI Airと同様、IPアドレスを固定とし、サーバーからFlashAirに対して写真をダウンロードしに行き、サーバーからGoogle Photosにアップロードすることにしました。

 

まずはIPアドレスを固定します。

FlashAirのCONFIGで設定します。

/SD_WLAN/CONFIGに[Vender]とは別に新たに[WLANSD]というセクションを設けて、IPアドレス等を指定します。

私の場合は192.168.x.161としました。(xは皆さんの環境に合わせてください。)

ルーターは192.168.x.1です。

IDは適当な名称をつけてください。

 [WLANSD]

ID=Flashair_999
DHCP_Enabled=NO
IP_Address=192.168.x.161
Subnet_Mask=255.255.255.0
Default_Gateway=192.168.x.1
Preferred_DNS_Server=192.168.x.1
Alternate_DNS_Server=8.8.8.8

 

で、STAモード(インフラストラクチャモード、つまり子機となってWi-Fiアクセスポイントに接続しにいくモード)で接続します。

私は次のサイトを参考にさせてもらいました。

database-tearoom.seesaa.net

そして、サーバー側のスクリプトを書きます。

手順としては、192.168.x.161を死活監視します。

もしhttpの応答があったら、/DCIM直下にあるindex.htmlの中から指定した名称が入ったフォルダのリストを抽出して配列に格納します。

通常、カメラは/DCIMの配下にカメラの機種名かメーカー名が入ったフォルダを作成し、その配下に9999枚までのファイルを保存していきます。たとえば私の場合はxxxEOS7Dという感じ。xxxは数字3桁ですね。

これをキーとして抽出することで、FlashAirの制御用画像を対象外にすることが出来ますし、どれだけフォルダが出来ても全てスキャン出来ます。

それぞれの配下のindex.htmlからファイルリストを取得します。

ファイルを一つづつflashair_done.txtの中にあるかないかをgrepで検証し、あれば処理を無視、なければwgetでFlashAirからダウンロード。

そして2048pxに縮小、Google Photosにアップロード、flashair_done.txtに書き出し、exiftoolで日付ディレクトリに移動させて、次のファイルの処理を行います。

 

アップロードの処理については前回PQI Airの際に作成した"upload_images_in_dir.sh"に任せます。

gentoolinux.hatenablog.com

 

必要なアプリケーションとしてはsedwgetImagemagick、exiftoolです。

 

スクリプトはこうなりました。

FLASHAIR_IP=にはFlashAirのIPアドレスを、

DCIM_FOLDER=にはカメラがDCIMの配下に作成するファイル名の3桁の数字を除いた文字列を入れてください。

 

#!/bin/bash

#初期変数一覧

#Flashairのアドレス
FLASHAIR_IP="http://192.168.x.161"

#DCIMフォルダの名称
DCIM_FOLDER="CANON"


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

#プロセスIDを保存
echo $$ > pid.txt

#DCIMフォルダのURLを生成
WGET_PRE_URL=${FLASHAIR_IP}"/DCIM/"

 

while :
do

#Flashairを死活監視
ALIVE=$(/usr/bin/wget -nv --spider --timeout 60 -t 1 ${FLASHAIR_IP} 2>&1 | grep -c '200 OK')

#もし死活監視で生きていたら
if [ $ALIVE -eq 1 ]; then

#配列を初期化
echo "Flashair - Format Arrys."
DCIM_LIST=()
JPG_LIST=()

#DCIM配下のディレクトリを配列に代入
echo "Flashair - Get under DCIM directory list."
DCIM_LIST=(`/usr/bin/wget ${WGET_PRE_URL} -q -O - | grep 'wlansd.push({"r_uri"' | sed -e 's/wlansd.push({"r_uri":"//' -e 's/", "fname":"/\//' -e 's/", "fsize".*//' | grep ${DCIM_FOLDER}`)

#フォルダの数だけループ処理を実施
echo "Flashair - Starting loop under DCIM directory."
DCIM_LOOP=${#DCIM_LIST[@]}
DCIM_LOOP=$*1
while [ $DCIM_LOOP -ge 0 ];
do

#フォルダの中のファイルを配列に代入
echo "Flashair - Get file list under "${DCIM_LIST[DCIM_LOOP]}
JPG_LIST=(`/usr/bin/wget ${FLASHAIR_IP}${DCIM_LIST[${DCIM_LOOP}]} -q -O - | grep 'wlansd.push({"r_uri"' | sed -e 's/wlansd.push({"r_uri":"//' -e 's/", "fname":"/\//' -e 's/", "fsize".*//'`)

#ファイルの数だけループ
echo "Flashair - Starting loop under "${DCIM_LIST[DCIM_LOOP]}
JPG_LOOP=${#JPG_LIST[@]}
JPG_LOOP=$*2
while [ $JPG_LOOP -ge 0 ];
do

#アップ済みのファイルかどうかをチェック
echo "Flashair - Check FLASHAIR_DONE "${JPG_LIST[JPG_LOOP]}
if ! `cat flashair_done.txt | grep -q "${JPG_LIST[JPG_LOOP]}"` ; then

#アップ済みになかった場合の処理
#wgetによるファイルの取得
echo "Flashair - Get file "${JPG_LIST[JPG_LOOP]}
/usr/bin/wget -q -N --no-host-directories --no-directories ${FLASHAIR_IP}${JPG_LIST[JPG_LOOP]}

#ダウンロードした全てのJPGを2048pxにリサイズ
echo "Flashair - Resize "${JPG_LIST[JPG_LOOP]}
/usr/bin/mogrify -resize 2048x2048 -quality 100 *.JPG

#ディレクトリ上にあるJPGファイルをpicasaweb APIでアップロード
echo "Flashair - Uploading "${JPG_LIST[JPG_LOOP]}
./upload_images_in_dir.sh

#ディレクトリ上にあるJPGファイルを日付ディレクトリで整理
echo "Flashair - Move YMD directory "${JPG_LIST[JPG_LOOP]}
/usr/bin/exiftool -q '-Directory < CreateDate' -d %Y%m%d *.JPG

#flashair_done.txtにファイル名を追加
echo "Flashair - Output to flashair_done.txt "${JPG_LIST[JPG_LOOP]}
echo ${JPG_LIST[JPG_LOOP]} >> flashair_done.txt

fi

JPG_LOOP=$*3
done


DCIM_LOOP=$*4
done

fi

sleep 10
done

 

 

ちなみに、upload_images_in_dir.shの中身はこんな感じです。

#/bin/bash

CLIENT_ID="xxxxxx.apps.googleusercontent.com"
CLIENT_SECRET="yyyyyy"
REFRESH_TOKEN="zzzzzz"
USER_ID="" #googleアカウントのユーザー名
ALBUM_ID="aaaaaaaaa(全て数字)"

#アクセストークン取得
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="./" # 任意のディレクトリを指定

IFS_BAK=${IFS}
IFS="
"
FILES=`ls -1 ${DIR}`
for FILE in ${FILES}
do
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

 

うーん。このインデントがなくなるの、なんとかならないかなぁ。

*1:DCIM_LOOP - 1

*2:JPG_LOOP - 1

*3:JPG_LOOP - 1

*4:DCIM_LOOP - 1

SonoffとESP EasyでHomekit対応格安温度・湿度センサーをつくる

さて、以前にHomebrideでマイナス表示をするための設定を書きました。

gentoolinux.hatenablog.com

よく考えたら、この記事の元になる気温を取得するデバイスを紹介していなかったことに気づきました。

制作過程を含めて紹介します。

 

まず、Amazonで次の品物を入手しました。

 おなじみのSonoffです。

今回はリレーとして使うわけではありません。もったいないかもしれませんが、ケースもACアダプタも別途用意する必要がないため、かえって割安になると判断しました。

 

 温度湿度センサーのDHT22です。安い割に精度がそこそこ良いそうです。

 

f:id:naoyukinagano:20171214225017j:plain

屋内用と屋外用に2セット購入。

(右上のUSBタイプLEDむき出しのライトはオマケ)

他に、ジャンパー線や10kΩ程度の抵抗、電源ケーブルとコンセントが必要ですが、ありもので済ませました。

 

まずはピンヘッダーを半田付けし、ESP Easyを書き込みます。

詳しくは以前の記事を

gentoolinux.hatenablog.com

このときは、リレーを使って照明をON/OFFさせていましたが、今回はあの、余っているGPIOピンを使います。

使うのは、5V、GND、GPIO14の3つ。

f:id:naoyukinagano:20171214225801j:plain

これは、赤が5V-VCC、グレーがGND、青がGPIO-14です。

 

DHT-22側に5V-VCCとDATA端子の間に10kΩのプルアップ抵抗をつけておきます。

f:id:naoyukinagano:20171214230112j:plain空中半田なので、かなり汚いですね。

こんなことにならないよう、基板が付いたDHT-22の購入をお勧めします。

 

たとえばコレでしょうか?

 

で、コンパクトになるように、ケースの横にネジ止めします。

使ったネジは余っていた何かの残りのネジです。

f:id:naoyukinagano:20171214230507j:plain

あまり長いとケースを突き抜けて基板を痛めるし、短いと届きません。

 

電源ケーブルなどを接続して完成したのがこれ。

f:id:naoyukinagano:20171214230605j:plain

 

では、ESP Easyの設定です。IPアドレス等は設定されている前提です。

まず、Hardwareページです。

f:id:naoyukinagano:20171214231702p:plain

ほぼDefaultのままですが、WiFi Status Led:をGPIO-13に割り当てて、LEDがWiFiのステイタスを表示するようにしました。

 

続いてDevicesページのTask1をEditボタンを押してDeviceを定義します。

f:id:naoyukinagano:20171214231905p:plain

Device:のドロップダウンから、"Temperature & Humidity - DHT"を選択すると、設定項目がずらっと出てきます。

Nameにはわかりやすい名前を、Deley:は60のままいじらず、IDX / Var:は適当に他とかぶらないように(といっても、MQTTプロトコルで利用する数値なので、HTTPで使う場合は関係ありません。)、1st GPIOはDHT-22を接続している"GPIO-14"を選択、DHT Type:は接続しているDHT-22を、Send Data:はHTTPで使うならチェックの有無どちらでも良い気がします。

下段のFormula temperature:は、気温の補正式を入れます。センサーから読み取った値が”%value%”という変数に入っています。これに対し、四則演算などで補正して出力することが可能です。私の環境では、ベランダに置いているのですが、室内の熱が伝わるせいで、アメダスの気温よりも高めに表示されるため、7度低く出力されるように”%value%-7”を入力しています。Decimals:は小数点第何位まで出力するかです。

Formula humidity:は湿度の補正式ですが、特に補正していません。

Value Name 1:は、JSON形式で出力する際の左辺の文字列です。デフォルトでは"Temperature"と、先頭が大文字ですが、homebridgeのHTTP気温センサプラグインが"temperature"と、小文字で認識するようなので、"temperature"と、小文字に変更しています。

Value Name 2:も同様に"humidity"と小文字にしています。

 

Submitで確定しCloseでDevicesに戻り、Task 2をEditします。

f:id:naoyukinagano:20171214235946p:plain

Task2は、Sonoffについているスイッチを、リブートスイッチにするというもので、たいして重要ではありません。

上記の通り設定してSubmitしCloseで閉じます。

 

できあがったDevices画面がコチラ。

f:id:naoyukinagano:20171215000119p:plain

右側にtemperature:とhumidity:の値が表示されていれば成功です!

 

あとは、リブートスイッチで再起動するように、Rulesに以下の記述をします。

on rebootswitch#Switch do
reboot
endon

 

これでSonoffとESP Easyによる温湿度センサーは完成です。

この気温と湿度をiOSのHomekit(ホームアプリ)に表示させるべく、homebridgeにプラグインを組み込んで設定します。

使ったプラグインは、"homebridge-http-temperature-humidity"です。

www.npmjs.com

まずはインストール。

gentoo # npm install -g homebridge-http-temperature-humidity 

で、config.jsonに設定を書きます。

ESP EasyにはDevicesのTask番号を指定することで、その値をJSON形式で返してくれる機能があります。これを利用します。(urlのxとyは環境に合わせて変えましょう。)

{
"accessory": "HttpTemphum",
"name": "Outside Temperature",
"url": "http://192.168.x.y/json?tasknr=1"
}

 accessoryの値が" homebridge-http-temperature-humidity"ではなく"HttpTemphum"ということに注意します。

ちなみにこのhttp://192.168.x.y/json?tasknr=1にブラウザからアクセスすると

TaskName: "OutsideTemp"
temperature: -3.8
humidity: 62.5 

 とこんな風に返ってきます。この値をhomebridgeが読み取って、iOSのHomekitに返しているようです。

homebridgeを再起動してiOSのHomekitを見てみると・・・。

f:id:naoyukinagano:20171215001800j:plain

気温と湿度が表示されました。ちなみに、小数点は四捨五入するようです。

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つのスイッチを同時に消灯することが可能になりました。