Bluetooth BLEに対応した各種センサーがSwitchbotから出ているので、それらの情報を取得しスマートホームの制御に用いたい。
Switchbotの純正アプリでもトリガー&アクションを設定できるが、Switchbot製品しか設定できないため、自分で実装しあとでNode-redに接続する。
ライブラリのインストール
$sudo apt-get install python3-pip libglib2.0-dev
$ pip install bluepy
bluepyにsudo権限を設定し、ユーザでも実行できるようにする。詳細はこちら
$find /usr/local/lib -name bluepy-helper
$find ~/ -name bluepy-helper
$cd 上記のコマンドで確認したインストールPATH
$sudo setcap 'cap_net_raw,cap_net_admin+eip' bluepy-helper
Switchbotの通信プロトコル
Switchbotから送られてくるデータの形式の詳細は、こちらに記載されています。
例えば、開閉センサー(Contact sensor)は、RESP Packet(下記プログラム中の"servicedata")の下記を見れば情報が分かります。
- Byte:1の7bit目: PIR(受動型赤外線センサ)による動体検知結果(0=動きなし, 1=移動体あり)
- Byte:2の1~7bit: バッテリー残量[%]
- Byte:3の1bit目: 明るさセンサーによる照度状態(1=明るい, 0=暗い)
- Byte:3の2,3bit目: ドアの開閉状態(2=開けっ放し, 1=開いている, 0=閉まっている)
通信&データ解析
開閉センサー:Contact
リンク
Pythonのライブラリを使って、BLE通信を行いSwitchbotからの受信データ解析を行います。
下記のプログラムは、こちらを参考に実装しました。
import binascii
import sys
import influxdb
from bluepy.btle import Scanner, DefaultDelegate
import datetime
sensorType = ['meter'
,'curtain'
,'contact'
]
sensorAddr = ['aa:aa:aa:aa:aa:aa'
,'bb:bb:bb:bb:bb:bb'
,'cc:cc:cc:cc:cc:cc'
]
sensorPlace = ['2F'
,'1F'
,'Bedroom'
]
count = [0]
class ScanDelegate( DefaultDelegate ):
def __init__( self ):
DefaultDelegate.__init__( self )
def handleDiscovery( self, dev, isNewDev, isNewData ):
global count
#countの初期化がまだであれば、addrのデータ数で配列を初期化
if ( len(count) != len(sensorAddr) ):
count = [0] * len(sensorAddr)
#MACアドレスが検索対象だったら
if dev.addr in sensorAddr:
#対応するセンサーの配列番号を特定
sensorNum = sensorAddr.index(dev.addr)
#Scandataの読み込み
scanData = dev.getScanData()
for data in scanData:
#読み出したいデータが入っている配列だけ抽出して、データを解析
if ( data[0] == 22 ):
#同じaddrが2回来たら中断
if ( count[sensorNum] > 0 ):
print("----")
print("finish")
print(" type: " + sensorType[sensorNum] )
print(" place: " + sensorPlace[sensorNum] )
print(" addr: " + sensorAddr[sensorNum] )
print("----")
sys.exit()
count[sensorNum] += 1
#データ読み取り
servicedata = binascii.unhexlify( data[2][4:] )
#Contact sensor
if ( sensorType[sensorNum] == "contact"):
#https://www.core-da-core.com/switchbot-opensensor-raspberry-pi/
#https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/latest/devicetypes/contactsensor.md
pir = ((servicedata[1]) & 0b01000000) >> 6
battery = (servicedata[2]) & 0b01111111
isIlluminance = (servicedata[3]) & 0b00000001
isOpen = ((servicedata[3]) & 0b00000110) >> 1
print("----")
print("type: " + sensorType[sensorNum] )
print("place: " + sensorPlace[sensorNum] )
print("addr: " + sensorAddr[sensorNum] )
print(" > battery: " + str(battery)) #バッテリー残容量 [%]
print(" > pir: " + str(pir)) #移動体検知 0=動きなし, 1=移動体あり
print(" > isIlluminance: " + str(isIlluminance)) #照度状態 1=明るい, 0=暗い
print(" > isOpen: " + str(isOpen) ) #開閉状態 2=開けっ放し, 1=開いている, 0=閉まっている
else:
print("----")
print("type: " + sensorType[sensorNum] )
print(" > value" )
print(data[2])
else:
#他ののBLE端末
print("----")
print("addr: " + dev.addr )
scanner = Scanner().withDelegate( ScanDelegate() )
scanner.scan( 0 )
温湿度計:Meter
リンク
#Meter
elif ( sensorType[sensorNum] == "meter"):
#https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/latest/devicetypes/meter.md
battery = (servicedata[2]) & 0b01111111
temperature = ( (servicedata[4]) & 0b01111111 ) + ( (servicedata[3]) & 0b00001111 ) / float(10)
isTemperatureAboveFreezing = (servicedata[4]) & 0b10000000
if not isTemperatureAboveFreezing:
temperature = -temperature
humidity = (servicedata[5]) & 0b01111111
print("----")
print("type: " + sensorType[sensorNum] )
print("place: " + sensorPlace[sensorNum] )
print("addr: " + sensorAddr[sensorNum] )
print(" > battery: " + str( battery ) ) #バッテリー残容量 [%]
print(" > temperature: " + str( temperature ) ) #温度 [℃]
print(" > humidity: " + str( humidity ) ) #湿度 [%]
カーテン:Curtain
リンク
#Curtain
elif ( sensorType[sensorNum] == "curtain"):
#https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/latest/devicetypes/curtain.md
battery = (servicedata[2]) & 0b01111111
isMotion = ((servicedata[3]) & 0b10000000) >> 7
position = (servicedata[3]) & 0b01111111
lightLevel = ((servicedata[4]) & 0b11110000) >> 4
print("----")
print("type: " + sensorType[sensorNum] )
print("place: " + sensorPlace[sensorNum] )
print("addr: " + sensorAddr[sensorNum] )
print(" > battery: " + str(battery))
print(" > isMotion: " + str(isMotion)) #カーテンの移動状態 0=stationary, 1=movement
print(" > position: " + str(position)) #開閉状態 [%]
print(" > lightLevel: " + str(lightLevel)) #LightLevel (1-10)
コメントを投稿