Raspberry pi zero wとhomebridgeとHomeKitの連携その2
先日、Raspberry Pi Zero Wで、リビングのライトのスイッチを、壁のプッシュスイッチからも、iOSのHomeKitからもON/OFFできるように、まずはRaspberry Piに接続したプッシュスイッチでリレーを、とりあえずON/OFF出来るようにしました。
しかし、このときはadd_event_detectをうまく使えず、while:ループの中で細かなsleepでごまかしながら力業で、プルアップされたGPIOインプットがGNDに落ちたかどうかを検出していました。
sleepを挟んでいるとはいえ、かなりCPUリソースを消費していたようです。
今は良いですが、夏は熱でスタックしそう・・・。
で、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つのスイッチを同時に消灯することが可能になりました。