SwitchBotをPCから操作するPythonスクリプト

PC前から1歩もうごくことなく暮らす。私はこの崇高なる目的を達成すべく、SwitchBot製品によるスマートホーム化とSwitchBot APIによるPCからの家電製品のコントロールを実現してきました。

これまでに、これとか

これとか

いろいろな手法でやってみた結果、やっぱりPythonが一番簡単だということが判明しました。(最初からわかっていました)。

今回はSwitchBotネタの最終章として、ついにPythonスクリプトでSwitchBot APIを実行できるようにしました。スクリプトを配布しますので、ご利用ください。

準備

SwitchBotのスマホアプリから機器が操作可能な状態になっている必要があります。

1.SwitchBotスマホアプリでPCから操作したい動作をすべてシーンにします。

2.SwitchBot APIを利用するためのトークンとクライアントシークレットを発行します。
スマホアプリのメニューからプロフィール → 設定 → アプリバージョンを10回くらいタップすると新たに開発者向けオプションが追加されるのでタップします。トークンを取得をタップするとトークンが表示されます。コピーをタップしてテキストファイルなどに保存しておきます。同様にクライアントシークレットも保存しておきます。

これらが他人に知られると、勝手に照明がついたり、TVのチャンネルが変わったりといったポルターガイスト現象の原因となるので、絶対に漏洩しないように厳重に管理してください。

使用方法

使用するPCにPython実行環境が必要です。

1.Pythonの requests モジュールが必要です。ない場合はインストールします。

pip install requests

2.このページのダウンロードボタンからスクリプト一式のzipファイルを入手します。

3.ダウンロードしたzipファイルを適当な場所へ展開します。このフォルダを以降アプリフォルダと呼称します。

4.アプリフォルダの中の bin フォルダにある main.py をテキストエディタ等で開きます。ファイルの文字コードはUTF-8です。

5.main.py の定数 TOKEN と SECRET に準備編で取得したトークンとクライアントシークレットを入力します。

import base64
import hashlib
import hmac
import json
import os.path
import pathlib
import time
import uuid
import requests

TOKEN = 'ここにトークンを入力'
SECRET = 'ここにシークレットを入力'


def generate_header():
# 以下略

6.main.py の変更を保存します。

7.main.py を実行します。アプリフォルダにシーン名のPythonスクリプトファイルができます。

9.シーン名のPythonスクリプトを実行すると、シーンの動作が実行されます。ショートカットをアクセスしやすい場所へ作成してアイコンをそれっぽく変えるとなお良いでしょう。

自作のなんちゃってSwitchBotアイコンを同梱していますのでご利用ください。

解説

main.pyのソースコードは次のとおりです。

import base64
import hashlib
import hmac
import json
import os.path
import pathlib
import time
import uuid
import requests

TOKEN = 'ここにトークン'
SECRET = 'ここにシークレット'


def generate_header():
    header = {}
    nonce = uuid.uuid4()
    t = int(round(time.time() * 1000))
    string_to_sign = bytes(f'{TOKEN}{t}{nonce}', 'utf-8')
    secret = bytes(SECRET, 'utf-8')
    sign = base64.b64encode(
        hmac.new(
            secret,
            msg=string_to_sign,
            digestmod=hashlib.sha256
        ).digest()
    )
    header['Authorization'] = TOKEN
    header['Content-Type'] = 'application/json'
    header['charset'] = 'utf8'
    header['t'] = str(t)
    header['sign'] = str(sign, 'utf-8')
    header['nonce'] = str(nonce)

    return header


def generate_scene_file():
    request_header = generate_header()
    url = 'https://api.switch-bot.com/v1.1/scenes'
    res = requests.get(url, headers=request_header)
    result = json.loads(res.text)
    for row in result['body']:
        fn = f"{row['sceneName']}.pyw"
        fp = os.path.join('..', fn)
        with open(fp, 'w', encoding='utf-8') as f:
            t = pathlib.Path('base.py').read_text(encoding='utf-8')
            f.write(t.replace('**sceneId**', row['sceneId']))


if __name__ == '__main__':
    generate_scene_file()

base.pyのソースコードは次のとおりです。

import requests
from bin.main import generate_header

scene_id = '**sceneId**'
header = generate_header()
url = f'https://api.switch-bot.com/v1.1/scenes/{scene_id}/execute'
res = requests.post(url, headers=header)

SwitchBot APIへのリクエストに必要なヘッダーの署名ほかの生成は公式サイトにサンプルコードがあるのでコピペするだけでよく簡単です。generate_headerとして関数にしておきます。

ヘッダーが作成できれば、シーン一覧を取得するURLへリクエストを送信すると、シーンの一覧がjsonで返ってきます。これにはsceneNameとしてシーン名が、sceneIdとしてシーンIDが格納されています。シーンIDはシーンを一意に指定するもので、APIからのシーン実行に必要です。

base.pyはAPIのシーン実行URLへリクエストを送信するスクリプトが書かれています。ただし、シーンIDを指定する部分は**sceneId**というプレースホルダーにしてあります。このファイルはテンプレートとして存在しており、そのまま実行してもエラーになります。次の手順でプレースホルダーを置き換えています。

generate_scene_file関数ではAPIからシーン一覧を取得したあと、それぞれのシーンに応じたシーン名とシーンIDをforループで取り出します。

次にbase.pyをpathlib.read_textメソッドを使用してテキストとして読み取り、replaceメソッドでプレースホルダー**sceneId**を一覧から取得した実際のシーンIDに書き換えます。その後、ファイル名をシーン名.pywとして保存します(.pywはコンソール画面を表示せずにPythonスクリプトを実行させるファイル形式です)。

これをシーンの数だけ繰り返し、シーン実行スクリプトが全シーン分作成されます。こちらのスクリプトでは、プレースホルダーが本物のシーンIDに書き換わっていますので、正しくリクエストされシーンの動作が実行されるというわけです。

スクリプトファイルを入手する

利用上のご注意

  • ダウンロードしたファイルを利用したことにより生じた結果については、利用者ご自身に責任を負っていただきます。
  • ご利用前に使用方法をご確認ください。
  • 当方は成果物の正確性について最善を尽くしますが保証はいたしません。
  • Windows11 Microsoft365 環境でのみ動作確認済み。

Downloadボタンを押下した時点で注意事項に同意したものとみなします。

switchbot_api_py.zip

実行結果をメッセージで表示する

APIが正常にコールできたのか、失敗したのかリアクションがほしい場合は「Windows3大うっとうしい機能」のひとつとしておなじみの、99%の人が無効にしているであろう通知機能を有効活用してメッセージを表示できます。

Pythonから通知機能を利用できるようにするplyerモジュールをインストールします。

pip install plyer

base.pyの末尾に次のコードを追加します。

import json
import os.path
from plyer import notification

result = json.loads(res.text)

if result['statusCode'] == 100:
    msg = f'{os.path.splitext(os.path.basename(__file__))[0]}が実行されました'
else:
    msg = '操作が失敗しました'

notification.notify(message=msg, app_icon='sb.ico')

main.pyを実行してシーン実行ファイルを再度作成します。

シーンを実行するとデスクトップ右下に例のあれが出て何を実行して成功したのか失敗したのかがわかります。

まとめ

今回のPythonが他のどの方法よりも、コードがシンプルでかつ実行速度も速いです。唯一、Python実行環境がWindowsでは標準搭載されていないという点だけが導入の障壁となるでしょうか。Microsoft様はやくPythonをOSバンドルにしましょうよ。

今回、APIから実行できる操作をシーンに限定しました。APIには機器単体に対してコマンドを送るエンドポイントがあります。実際にVBSやPowerShell版ではコマンドを送信する方法も用意しました。しかし、コマンド実行の場合はリクエスト時に設定する項目が増え、それらわざわざ調べる必要があります。その手間でスマホアプリからシーンを作成したほうが楽という判断から、シーンのみにしています。

最後にSwitchBot様へ。もうこれなしでは生活できないので、今現在どうやってAPIサーバーの運営費を調達しているのかわかりませんが、ある日突然サービスを終了しないでください。月額課金にするなら払いますので。

おわり。