住まいのIoT化に向けて導入したSwitch BotですがBluetoothでの通信なので そのままだと外出先とかから操作できません。 SwitchBot Hub Plusを買えばできるようになりますが、これはスマートリモコンの機能もあり すでに別なもの(Nature Remo mini)で出来ているのでできれば買いたくありません。
ということで自作を楽しむという意味でもRaspberry Piを使って ネットワーク経由で操作できるようにしてみたいな、と。
とりあえずRaspberry PiからSwitch Botを操作するところまで。
Raspberry Piの準備
この前セットアップしたRaspberry Pi Zero WHを使います。
WHにはWi-FiやBluetooth機能はもともと付いているので特に用意するものはありません。
ディスクとしてmicroSDとmicroUSBによる充電で動く状態であれば出来ます。
ということでOSをインストールして準備が出来たらやってみます。
python-host
Switch Botを作っているWonderLabsが公式にAPIを公開しています。
Python2用の準備
Gitをインストール:
$ sudo apt install -y git
pipをインストール:
$ sudo apt install -y python-pip
python-hostのREADMEに従い、必要なライブラリーなどをインストール:
$ sudo apt install -y python-pexpect
$ sudo apt install -y libusb-dev libdbus-1-dev libglib2.0-dev
$ sudo apt install -y libudev-dev libical-dev libreadline-dev
$ sudo pip install bluepy
レポジトリをとってくる:
$ git clone https://github.com/OpenWonderLabs/python-host.git
$ cd python-host
$ sudo python switchbot.py
root権限を使うらしくこの実行にもsudoが必要です。
Python3用の準備
Python2で使っているBluez utilsの中で使っているものがすでにBluez utilsの中でdeprecatedな状態 なものになっていてあまりよろしくない。
もうすぐPython2のサポートも来れることだし 用意されてるPython3用のスクリプトを使ったほうが良い。
pipをインストール:
$ sudo apt install -y python3-pip
このまま公式通りにpybluez
をpipでインストールしようとすると、
bluez/btmodule.h:5:10: fatal error: bluetooth/bluetooth.h: No such file or directory
というエラーが出てしまうのでlibbluetooth-dev
をインストール。
$ sudo apt install -y libbluetooth-dev
あとは公式通り:
$ sudo pip3 install pybluez
$ sudo apt install -y libboost-python-dev
$ sudo apt install -y libboost-thread-dev
$ sudo pip3 install gattlib
と、最後のgattlib
のインストール時に、
$ sudo pip3 install gattlib
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting gattlib
Using cached https://files.pythonhosted.org/packages/be/2f/5b1aecec551b42b59d8b399ad444b5672972efb590ca83d784dbe616a3e1/gattlib-0.20150805.tar.gz
Building wheels for collected packages: gattlib
Running setup.py bdist_wheel for gattlib ... |
の状態で固まって数十分後?にRaspberry Piが落ちてしまう、という状態に。
再度アクセスしようとしても駄目で、電源口横の緑色のボタンも素早く点滅している状態でどうやら何か駄目っぽい。 一度電源ケーブルを抜き差しして再起動。
その後で、公式にもある通り、pipで失敗した場合の別のインストール方法があるのでそれを試してみる。
$ sudo pip3 download gattlib
$ tar xvzf ./gattlib-0.20150805.tar.gz
$ cd gattlib-0.20150805/
ここでsetup.pyの書き換えをsedで行っていますが、python34の設定をpython35にしています。 これを行う前に実際にインストールされているバージョンを確認。
$ python3 --version
Python 3.7.3
とうことで3.7なのでpy34ではなく、py37。さらにpy37の場合は
boost_python-py37
ではなくboost_python3-py37
という名前になっています。
$ ls /usr/lib/arm-linux-gnueabihf/libboost_python*
/usr/lib/arm-linux-gnueabihf/libboost_python27.a
/usr/lib/arm-linux-gnueabihf/libboost_python27.so
/usr/lib/arm-linux-gnueabihf/libboost_python27.so.1.67.0
/usr/lib/arm-linux-gnueabihf/libboost_python37.a
/usr/lib/arm-linux-gnueabihf/libboost_python37.so
/usr/lib/arm-linux-gnueabihf/libboost_python37.so.1.67.0
/usr/lib/arm-linux-gnueabihf/libboost_python3.a
/usr/lib/arm-linux-gnueabihf/libboost_python3-py37.a
/usr/lib/arm-linux-gnueabihf/libboost_python3-py37.so
/usr/lib/arm-linux-gnueabihf/libboost_python3.so
/usr/lib/arm-linux-gnueabihf/libboost_python.a
/usr/lib/arm-linux-gnueabihf/libboost_python.so
python.a
などはpython2
系のもの、python3
系のものはすべて
libboost_python37.so
を経由して
libboost_python37.so.1.67.0
へのシンボリックリンクになっています。
libboost_python3.so
もそうなのでこれにしておけばPythonのバージョンが変わっても動く。
(中身が互換性がないとだめですが。)
ので
$ sed -ie "s/boost_python-py34/boost_python3/" setup.py
とします。(単に開いてboost_python-py34
の部分を書き換えても良いです。)
準備ができたらインストール:
$ sudo pip3 install .
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Processing /home/pi/gattlib-0.20150805
Building wheels for collected packages: gattlib
Running setup.py bdist_wheel for gattlib ... \
これまた同じ様なところで進みません。
途中で他の接続をして見てみると
$ top -n1
top - 02:35:54 up 6 min, 2 users, load average: 1.16, 0.68, 0.30
Tasks: 81 total, 2 running, 79 sleeping, 0 stopped, 0 zombie
%Cpu(s): 82.8 us, 17.2 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 432.7 total, 30.0 free, 350.0 used, 52.8 buff/cache
MiB Swap: 100.0 total, 92.2 free, 7.8 used. 32.6 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
506 pi 20 0 343212 314036 14240 R 73.9 70.9 0:50.90 cc1plus
508 pi 20 0 10172 2828 2412 R 17.4 0.6 0:00.10 top
1 root 20 0 32584 7424 6308 S 0.0 1.7 0:06.10 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd
5 root 20 0 0 0 0 I 0.0 0.0 0:00.02 kworker/u2:0-events_unbound
6 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 mm_percpu_wq
7 root 20 0 0 0 0 S 0.0 0.0 0:00.39 ksoftirqd/0
8 root 20 0 0 0 0 S 0.0 0.0 0:00.01 kdevtmpfs
9 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 netns
10 root 20 0 0 0 0 I 0.0 0.0 0:00.39 kworker/0:1-events
$ free
total used free shared buff/cache available
Mem: 443100 364068 28456 2604 50576 27868
Swap: 102396 8192 94204
$
といった感じでどんどんメモリ使用量が増えていってメモリが足りないようです。
メモリは~400MB、Swapも100MBしか用意されてないようなので、ここで一度思い切ってSwapを増やしてみます。
$ cat /etc/dphys-swapfile
# /etc/dphys-swapfile - user settings for dphys-swapfile package
# author Neil Franklin, last modification 2010.05.05
# copyright ETH Zuerich Physics Departement
# use under either modified/non-advertising BSD or GPL license
# this file is sourced with . so full normal sh syntax applies
# the default settings are added as commented out CONF_*=* lines
# where we want the swapfile to be, this is the default
#CONF_SWAPFILE=/var/swap
# set size to absolute value, leaving empty (default) then uses computed value
# you most likely don't want this, unless you have an special disk situation
CONF_SWAPSIZE=100
# set size to computed value, this times RAM size, dynamically adapts,
# guarantees that there is enough swap without wasting disk space on excess
#CONF_SWAPFACTOR=2
# restrict size (computed and absolute!) to maximally this limit
# can be set to empty for no limit, but beware of filled partitions!
# this is/was a (outdated?) 32bit kernel limit (in MBytes), do not overrun it
# but is also sensible on 64bit to prevent filling /var or even / partition
#CONF_MAXSWAP=2048
$ sudo sed -i -e "s/^CONF_SWAPSIZE=.*/CONF_SWAPSIZE=2048/g" /etc/dphys-swapfile
$ sudo reboot
再起動後もう一度sudo pip3 install .
に挑戦。
$ sudo pip3 install .
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Processing /home/pi/gattlib-0.20150805
Building wheels for collected packages: gattlib
Running setup.py bdist_wheel for gattlib ... done
Stored in directory: /root/.cache/pip/wheels/2b/2d/d2/d62bdc868bb470d7b7e2fa672805652c9e57ff271d2ba6a8ea
Successfully built gattlib
Installing collected packages: gattlib
Successfully installed gattlib-0.20150805
うまくいきました。結構(10分~20分くらい?)かかりましたがなんとか終わりました。 別のモニタで見てたところ、実メモリを使い切ってswapも200MBくらいは使ってたので 2GBまでは必要なかったですがやはり100MBではたりなかった模様。
これでswitchbot_py3.pyスクリプトが呼べるようになりました。
$ cd ~/python-host
$ python3 switchbot_py3.py --help
usage: switchbot_py3.py [-h] [--scan] [--scan-timeout SCAN_TIMEOUT]
[--connect-timeout CONNECT_TIMEOUT] [--device DEVICE]
[--interface INTERFACE]
optional arguments:
-h, --help show this help message and exit
--scan, -s Run Switchbot in scan mode - scan devices to control
--scan-timeout SCAN_TIMEOUT
Device scan timeout (default: 2 seconds)
--connect-timeout CONNECT_TIMEOUT
Device connection timeout (default: 5 seconds)
--device DEVICE, -d DEVICE
Specify the address of a device to control
--interface INTERFACE, -i INTERFACE
Name of the bluetooth adapter (default: hci0 or
whichever is the default)
実際に使ってみる
この前設置したSwitch Botを動かしてみます。
とりあえずスクリプトにはSwitch Botを探せる機能もあるみたいなのでそれを試します。
Python2:
pi@raspberrypi:~/python-host $ sudo python switchbot.py
Usage: "sudo python switchbot.py [mac_addr cmd]" or "sudo python switchbot.py"
Start scanning...
scan timeout
No SwitchBot nearby, exit
Python3:
pi@raspberrypi:~/python-host $ sudo python3 switchbot_py3.py --scan
No Switchbots found
Python2/3バージョン共に見つからない。
パスワードがかかっているのが駄目なのか?と思いパスワードを外してみたもののやはり見つからない。
試しにhcitool
を使ってBluetoothが見えているかどうか確かめてみると。
$ sudo hcitool lescan
LE Scan ...
XX:XX:XX:XX:XX:XX (unknown)
XX:XX:XX:XX:XX:XX (unknown)
...
みたいな感じでBluetoothは使えている様に見える。
だいぶ前の(2年前)だが、この時点ですでにSwitch Botの仕様が変更され、
hcitool lescan
でなんのデバイスか分からないようになったため
スクリプトのScanは使えなくなっているらしい。
中の人によると、以下のコードも見てみてね、とのことである。
http://codegist.net/snippet/python/switchbot_bluepypy_aerialist_python
gatoolもpexpectも使わずに、より簡潔に制御できるのでこっちのほうがいいね。
とありますが、これはリンク切れでした。
上のレポジトリのPython2バージョンは2年前、 Python3の方でも1年前のコミットでこの辺分かった後の話だとは思いますが、 現状BluetoothからSwitchBotかどうか分かる手段はないのでScanでは無理(手当り次第やる、というてはありますが)。
ので直接BLE Macアドレスを調べます。
BLE Macアドレスの確認はアプリの中の 各SwitchBotの設定画面に行き、右上の3ボタンを押すと出てきます。
こんな感じで表示されてるSwitchBot一覧で各Botの右上にある歯車をタップし
SwitchBotの設定画面で右上の3点ボタンを押します。 (昔は下の設定一覧の所にBLE MACアドレスがあったみたいですが、妙にこれだけわかりにくくなってます。)
そうするとこんな感じでBLE MACアドレスを見ることが出来ます。
このアドレスを使って試してみます。
Python2:
$ sudo python switchbot.py "XX:XX:XX:XX:XX:XX" Press
Usage: "sudo python switchbot.py [mac_addr cmd]" or "sudo python switchbot.py"
Preparing to connect.
Trigger complete
Python3:
$ sudo python3 switchbot_py3.py -d "XX:XX:XX:XX:XX:XX"
Connected!
Command execution successful
(process:1599): GLib-CRITICAL **: xx:xx:xx.xxx: Source ID 4 was not found when attempting to remove it
の様にどうやら成功したようです。
Python3版の方でCRITICALなメッセージが出ていますがとりえず無視して大丈夫そうです。 バグ?1
Python2の場合にはPress
、Turn On
、Turn Off
のサブコマンドが使えます。
Python3のswitchbot_py3.pyの場合にはpress
がハードコードされてるので、
press
しか送れませんが、ちょっと改造して--command on
(or off
)で
on
やoff
も送れるように改造したものを作ってみました。
python-host/switchbot_py3.py at command · rcmdnk/python-host
とりあえずこれで成功している用に見えますが、 両方ともパスワードを設定していないときは上手くいきました。
パスワードを設定すると上と同じメッセージが出ますが、Switch Botは動きません。
そもそもパスワードを送るような 部分がスクリプトの中にないのでどうしたもんかと調べてみると、
Trigger SwitchBot via API/Bluetooth directly? SwitchBot Community SwitchBot
どうやら今の所パスワード認証に関するAPIは公開してないようです。 ので、現状ではパスワードなしの状態でやらないとアプリ以外からは操作できません。
多分公式の記述は間違いで10mくらいまでしかBluetooth通信は届かないので、隣近所の人が もしSwitch Botを使っていたら使われてしまうかも、という危険性はあります。 が、その場合にはこちら側のアプリにも相手側のSwitch Botがあらわれるはずです。 (たまたま他の家でSwitchBotを使っていてアプリを入れていて、隣のウチに遊びに来た、とかだとその人のアプリに出てしまうことはありますが、 そこでアプリをチェックすることは少ないはず。。。)
ということで、今の所周りに他のSwitch Botは見当たりませんし、パスワード無しでも大丈夫かな。。。
換気扇の方は別にいじられても何も問題ないわけですが、 マンションの解錠の方はどうかな、と思ったりもします。
ですが、実際入り口からはBluetoothで繋がりませんでしたし 届くとしたらすでにマンションの中からかな、という感じなのでそこまで心配する必要はないかな、とも。
Raspberry Piである意味
ここまでの操作であれば単にLinuxの動くPCでBluetoothデバイスが載っていれば出来ます。
WindowsだとWindows Subsystem for Linuxから使えるかな、と思いましたがBluetoothデバイスを扱えませんでした。
とりあえず現在は無理そう。
Bluetooth not supported (Built-in and USB-BT Adapter) · Issue #242 · microsoft/WSL
WSL2がすでに公開されているようですが、Linuxカーネルが動くようになり大分様相が変わるので もしかしたらいろいろなデバイスへのアクセスなども出来るようになるのかもしれません。
完全なLinuxがWindows 10上で稼働する? 「WSL 2」とは:Windows 10 The Latest - @IT
また、Macではスクリプトはそのまま動きません。
スクリプトの中にService UUID(とManufacture ID)とコマンド(press/turn on/turn off)用の 信号キーがあるので、 これらを使ってWindowsでもMacでもBluetoothを操作するスクリプトを作れば同じ様なことは出来るはずです。(やってない)
自宅とかで常に起動しているマシンがあるならそれを使ってももちろん良いと思います。
ネットワークからの操作
とりあえずここまででRaspberry PiからSwitch Botを操作できるようになりました。
あとはネットワークから操作できるようにして、Google Homeとかからも使えるようにしたいと思います。