feature image

2025年3月11日 | ブログ記事

Raspberry Pi から SwitchBot(ボット)を操作してみる

この記事は新歓ブログリレー2025 5日目の記事です。

こんにちは。24BのSyntaxErrorです。突然ですが、SwitchBotを試したいけどSwiwtchBotのハブを買うのはちょっと…と思ったことはありませんか?僕はあります。そこで、どの家にもあるRaspberryPiからSwitchBot(ボット) (以下Bot)を操作してみました!

今回のコードの一部はGitHubにありますので、もしよろしければ参考にしてみてください。
なお、十分にスキルがあるわけではないのでクオリティはお察しください…

使ったもの

やったこと

APIドキュメントを見る

SwitchBotはBLE(Bluetooth Low Energy)のドキュメントをGitHubで公開しています。
https://github.com/OpenWonderLabs/SwitchBotAPI-BLE
これを読むとBotを操作できます。

言語選択

Raspberry Piで動けばなんでもいいので、なんとなくGoを選択。自分のGoの練習も兼ねています。Goでなければならない理由は特にないです。
Goでbluetoothを扱うためにはgo-bleを使いました。

がんばる

SwitchBotについても、BLEについても十分に知識があるわけではないのでひたすら試行錯誤をしました。
ボットのドキュメントにはすべてが書いてあるので、調べながら実装しました。
以下は試行錯誤してなんとか動くようになったやり方です。

initialize

go-bleをlinuxで利用するときは、go-ble/linux内のNewDevice()で得られるdeviceをble.SetDefaultDevice(device)で設定します。

Scan

Scanを行い、Bot Broadcast Messageを受け取ります。
func Scan(ctx context.Context, allowDup bool, h AdvHandler, f AdvFilter) errorでは、範囲内のBluetoothデバイスからのアドバタイズを受信し、AdvFilterでフィルタリングして、それぞれのアドバタイズに対してAdvHandlerを実行します。
Botのみに絞るためには、アドバタイズにServiceUUID0xfd3dのServiceが存在し、Service Dataの最初のbyteが0x48または0xc8であることを確認するのがいいと思います。(0xfd3dはWoanTechnology(参考)、0x48SwitchBot Bot(WoHand)0xc80x48の暗号化された状態です。)
さて、ScanによってアドバタイズからはMACアドレスが、Service DataからはSwitchBotのモードや状態、バッテリーの残量などが得られます。操作するときにはMACアドレスとBotのモードや状態が必要なので、適切に保存しておきます。

Botへの接続

ble.Connectble.Dialを用いて接続します。少なくとも自分の環境においてDialはScan中にしか動かなかったので、Connectのほうが使い勝手がいい気がします。
DialはMACアドレスを、Connectはアドバタイズが条件を満たすかを返す関数を引数に与えれば動きます。

Characteristicの取得

ドキュメントを読むと、サービス(UUID:cba20d00-224d-11e6-9fb8-0002a5d5c51b)のキャラクタリスティック(UUID:cba20002-224d-11e6-9fb8-0002a5d5c51b、書き込み用)にREQ messageを書き込むといいよと書いてあるので、それを実装します。
client.DiscoverServicesを用いてサービスを取得し、client.DiscoverCharacteristicsを用いてキャラクタリスティックを取得します。
同時にもう一つのキャラクタリスティック(UUID:cba20003-224d-11e6-9fb8-0002a5d5c51b、Notify用)も取得しておくと、実行結果を取得できます。このキャラクタリスティックはNotifyを受け取るときに使います。

Characteristicへの書き込み

ClientWriteCharacteristic(c *Characteristic, value []byte, noRsp bool) errorを用いてREQ messageを書き込みます。
cには先ほど取得した書き込み用のキャラクタリスティックを指定します。
valueにはドキュメントの通りにバイト列を設定します。

とすればよいです。
noRspはfalseにしておくとerrorがちゃんと返ってくるのでfalseのほうがいいと思います。

これが正常に実行されれば、SwitchBotがボタンを押すなり引くなりしてくれます。
自分はここまでに3日間かかりました。

暗号化について

Botにはパスワードを設定する機能が備わっています。パスワードが設定されている場合、残念ながら上の方法では操作できません。そこで、この記事を参考にしつつ、Bluetoothのログをとってみることにしました。
なお、自分のボットには2449というパスワードをかけていました(現在変更済み)。

ここから先は自己責任でお願いします。

頑張ってログを読む

CTFをします。
Androidスマホから取り出したログをWiresharkに入れ、それっぽいログを探します。
すると、次のようなログがありました。

Value: 5711bc2bd98501

Value: 5711bc2bd98501の部分が怪しいです。
元々のREQ messageのことを考えると、0x570x110xbc2bd9850x01のように分解できると予想できます。
ここで、0xbc2bd985がなにかしらの方法で暗号化されたパスワードであると予想し、様々な暗号化やエンコードを試せるサイトにパスワードである2449を入れてみると、CRC32による変換がちょうど同じになっています。

実際に、他のパスワードに変換しても同様にCRC32で変換されていることが確かめられます。
これを適切に実装するとパスワードが設定された状態でもスイッチを押すことができます。
エンディアンに注意しないと失敗します(1敗)。

いかがでしたか?

今回はSwitchBotのボットをRaspberryPiから動かしてみました!
12月に試したまま放置していたので、ここで記事にできてよかったです。

GitHubにはスイッチを動かす前に情報を取得してみているので、よかったらそちらもご覧ください!

明日は@kitsneさんによる記事です!お楽しみに~

SyntaxError icon
この記事を書いた人
SyntaxError

24B SysAd/Algo/Kaggleに所属しています

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2025年3月9日
# WOLF RPGエディターについて
Synori icon Synori
2025年3月12日
サークル旅行のためだけにアプリをこしらえる人たち
kitsne icon kitsne
2025年3月7日
新歓特設ページを公開しました!【新歓ブログリレー1日目】
Luke256 icon Luke256
2025年3月13日
traPを吸い尽くせ! (traP活用法紹介)
zoi_dayo icon zoi_dayo
2025年3月10日
たのしく単位をとろう!
Naru820 icon Naru820
2025年3月8日
traP部員による「PCの選び方」 2025年度版
ramdos icon ramdos
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記