rcmdnk's blog

SwitchBot スイッチボット スイッチ ボタンに適用 指ロボット スマートホーム ワイヤレス タイマー スマホで遠隔操作 Alexa, Google Home, Siri, IFTTTなどに対応(ハブ必要)

住まいの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は使えている様に見える。

Switch Bot が届いたのでSingle Board Computerから操作してみた - ブログ

だいぶ前の(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ボタンを押すと出てきます。

20190817_switchbot.png

こんな感じで表示されてるSwitchBot一覧で各Botの右上にある歯車をタップし

20190817_switchbotbotset.png

SwitchBotの設定画面で右上の3点ボタンを押します。 (昔は下の設定一覧の所にBLE MACアドレスがあったみたいですが、妙にこれだけわかりにくくなってます。)

20190817_switchbotblemac.png

そうするとこんな感じで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の場合にはPressTurn OnTurn Offのサブコマンドが使えます。

Python3のswitchbot_py3.pyの場合にはpressがハードコードされてるので、 pressしか送れませんが、ちょっと改造して--command on (or off)で onoffも送れるように改造したものを作ってみました。

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とかからも使えるようにしたいと思います。

Sponsored Links
Sponsored Links

« Raspberry Piの初期設定 Blynkを使ってRaspberryi Piをスマホから操作する »

}