SwitchBotをPCから操作するスクリプトを作成するExcelツール

私の部屋の家電製品の大半はGoogle Homeの支配下にあり、Googleアシ子ちゃんにお願いするとだいたいのことはやってくれます。

アシ子ちゃんにお願いするには「OK、Google」と話しかけなければならないのですが、私クラスの引きこもり能力保持者になると、3連休があれば72時間一切発声しないという生活が余裕でできるようになるので、必然的に「OK、Google」と言うのも面倒という事態に陥ります。

よって常々「PC(常時稼働)から家中の機器が操作できればいいのになぁ」と思っていたのですが、現代のテクノロジーを用いると、実は意外と簡単に実現できてしまうのです!

SwitchBot APIで家電を操作する

SwitchBotはIoT機器をたくさん販売しているメーカーです。詳しくはGoogle先生へ。

SwitchBot製品を導入することで、スマホアプリを家電のリモコン代わりにして操作したり、Google AssistantやAmazon Alexaと連携させることで、音声だけで操作ができたりします。

さらにSwitchBotはAPIを公開しており、Web経由でコマンドを送ることでスマホアプリと同等の操作が可能になります。ということは、これを使えばPCからアイコンをクリックするだけで機器の操作ができるようになるということです!

このAPIはHTTPリクエストをサーバーへ送信するだけなので、JavaだろうがPythonだろうが好きなのを使えばいいのですが、ここはあえてExcelとVBA(VBS)で行きたいと思います。

デモ動画

ということで、まずはデモ動画をご覧ください。私の所有する30万円のVAIOには後述のExcelツールで作成したスクリプトが保存してあります。

玄関照明スイッチにはSwitchBotのBotが取り付けてあります。美観についてはBot使うからには諦めます。

通常はSwitchBotの人感センサーと連動させ、玄関照明を自動点滅させているのですが、API経由でPCから操作するとこうなります。スイッチの動作がわかりやすいようにあえて環境音を入れていますのでご注意ください。

SwitchBotのアプリで操作できる機器はすべてAPIでも操作できるので、私の家では、この玄関照明の他にTV、エアコン、リビング照明、ダイニング照明、リビングカーテン、ダイニングカーテンをPCから操作することができます。

システム概要

しくみは非常に簡単で、APIに必要なパラメーターを付けたHTTPリクエストを送信するVBSをExcel-VBAでメタプログラミングで出力して、それを実行しています。

Excelツールを配布しますので皆さんもお試しください。

使用方法

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

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

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

2.このページのダウンロードボタンからExcelファイルを入手します。
ダウンロードしたzipファイルを展開してください。

3.展開したExcelファイルの「トークン」シートに1.でコピーしたトークンを貼り付けます。

4.「機器リスト」シートの機器リスト取得ボタンを押します。
あなたがSwitchBotで操作している機器が表示されます。

5.「コマンドジェネレーター」シートに操作したい機器のdeviceIdをコピペします。

6.deviceTypeによって指定できる操作が下へ一覧表示されるので参考にしてcommandType、Command、command parameterをそれぞれ入力します。
一覧表示はFilter関数を使用しているのでMicrosoft365をご利用の方のみ表示されます。表示できない場合は「commandlist」シートを参照するかSwitchBotAPIのページで確認してください。もしくはMicrosoftへ上納金を納めてください。

設定例をいつくか載せておくので参考にしてください。

設定例 Botの押す操作を実行

設定例 TVを1chにする

設定例 エアコンを25℃、冷房、風量自動でONにする

7.設定値を入力した状態でテストボタンを押すと、APIをコールします。
結果がtest resultに表示されます。実際に機器が動作すればOKです。

うまく動作しない場合は

commandTypeCommandcommand parameter
commandturnOndefault

はすべての機器に適用できる(たぶん)ので、この設定で電源をONにすることができるか確認してください。

8.VBS出力ボタンを押します。

Excelと同じフォルダに.vbsファイルが出力されます。vbsにはトークンがそのまま書き込まれているので第三者に公開しないように注意してください。このファイルを開くとAPIがコールされ操作が実行されます。

アイコンをそれらしくすれば完成です。SwitchBotのロゴ画像は配布されていなそうなので、ぽいのを自作するか、基本自分だけが使うのでどこかからコピってきましょう。ちなみに下の画像のは私がフォトショで40秒で作成したアイコンです。

シーンを実行したい場合は「シーン」シートのシーンリスト取得ボタンでシーン一覧を取得して対象のシーンのIDをsceneIDセルに入力します。

APIがVer1.0→1.1になった影響だと思われますが、私の環境では、この方法ではシーンが実行されなくなりました。たぶんスマホアプリを新バージョン対応にアップデートすると、その時点でVer1.0のシーンがAPIから走らなくなります。
新バージョン対応ツールはこちら。

シーンのテストとVBS出力については、前述の方法に準じます。自分でコマンドを組んでいくのが面倒な機器はスマホアプリでシーンにしてしまって、シーンを実行したほうが簡単かもしれません。

話はそれますが、エアコンはGoogleアシ子ちゃんに単に「エアコンつけて」と言うと、前回停止時の状態を完全無視して真冬でも冷房25℃とかで運転しだすポンコツぶりお茶目さを発揮するので、すべての条件を指定したシーンにしておいて、それを実行させるようにしたほうがよいです。

SwitchBotAPIのコール数の上限は1万回/日です。家電の操作だけであれば超過する方が難しいのではないかと思われますので、好きなだけ使ってください。

解説

ソースコードです。セルの値でHTTPリクエストのパラメーターを設定し送信する簡単なものです。

Option Explicit

Const HOST_DOMAIN = "https://api.switch-bot.com"

Enum deviceList
    deviceID = 1
    deviceName
    deviceType
End Enum

Enum sceneList
    sceneID = 1
    sceneName
End Enum

Function createHttp(method As String, url As String) As Object
    Dim token As String
    token = Worksheets("トークン").Range("b1").Value
    
    Dim http As Object
    Set http = CreateObject("MSXML2.XMLHTTP")
    
    http.Open method, url, False
    http.setRequestHeader "Authorization", token
    http.setRequestHeader "Content-Type", "application/json; charset=utf8"
    
    Set createHttp = http
End Function

Sub getSwitchbotDeviceList()
    Dim http As Object
    Set http = createHttp("GET", HOST_DOMAIN & "/v1.0/devices")
    http.send
    
    Do While http.readyState < 4
        DoEvents
    Loop
    
    Dim rb As Object
    Set rb = JsonConverter.ParseJson(http.responseText)("body")
    
    With Worksheets("機器リスト")
        Dim i As Long
        i = .Cells(Rows.Count, deviceList.deviceID).End(xlUp).Row + 1
        
        Dim dl
        Dim il
        'SwitchBot製品
        For Each dl In rb("deviceList")
            .Cells(i, deviceList.deviceID).Value = dl("deviceId")
            .Cells(i, deviceList.deviceName).Value = dl("deviceName")
            .Cells(i, deviceList.deviceType).Value = dl("deviceType")
            i = i + 1
        Next
        
        'HubでIr制御している機器
        For Each il In rb("infraredRemoteList")
            .Cells(i, deviceList.deviceID).Value = il("deviceId")
            .Cells(i, deviceList.deviceName).Value = il("deviceName")
            .Cells(i, deviceList.deviceType).Value = il("remoteType")
            i = i + 1
        Next
    End With
End Sub

Sub execCommand()
    Dim deviceID As String
    Dim commandType As String
    Dim command As String
    Dim commandParam As String
    deviceID = Range("a2").Value
    commandType = Range("b2").Value
    command = Range("c2").Value
    commandParam = Range("d2").Value
    Range("h1").Value = ""
    Range("h2").Value = ""
    
    Dim http As Object
    Set http = createHttp("POST", HOST_DOMAIN & "/v1.0/devices/" & deviceID & "/commands")
    
    Dim body As String
    body = "{""command"": """ & command & """, ""parameter"": """ & commandParam _
    & """, ""commandType"": """ & commandType & """}"
    http.send body
    
    Dim rt As Object
    Set rt = JsonConverter.ParseJson(http.responseText)
    
    Range("h1").Value = rt("statusCode")
    Range("h2").Value = rt("message")
End Sub

Sub getSwitchbotSceneList()
    Dim http As Object
    Set http = createHttp("GET", HOST_DOMAIN & "/v1.0/scenes")
    http.send
    
    Do While http.readyState < 4
        DoEvents
    Loop
    
    Dim rb As Object
    Set rb = JsonConverter.ParseJson(http.responseText)("body")
    
    With Worksheets("シーン")
        Dim i As Long
        i = .Cells(Rows.Count, sceneList.sceneID).End(xlUp).Row + 1
        
        Dim sl
        
        For Each sl In rb
            .Cells(i, sceneList.sceneID).Value = sl("sceneId")
            .Cells(i, sceneList.sceneName).Value = sl("sceneName")
            i = i + 1
        Next
    End With
End Sub

Sub execScene()
    Dim sceneID As String
    sceneID = Range("f2").Value
    Range("k1").Value = ""
    Range("k2").Value = ""
    
    Dim http As Object
    Set http = createHttp("POST", HOST_DOMAIN & "/v1.0/scenes/" & sceneID & "/execute")
    http.send
    
    Dim rt As Object
    Set rt = JsonConverter.ParseJson(http.responseText)
    
    Range("k1").Value = rt("statusCode")
    Range("k2").Value = rt("message")
End Sub

Sub commandVBS()
    Call exportVBS("command")
End Sub

Sub sceneVBS()
    Call exportVBS("scene")
End Sub

Sub exportVBS(mode As String)
    Dim name As String
    Dim s As Long
    Dim e As Long
    
    Select Case mode
        Case "command"
            With Worksheets("コマンドジェネレーター")
                name = .Range("b4").Value & "_" _
                & .Range("c2").Value
            End With
            s = 1
            e = 8
        Case "scene"
            name = Worksheets("シーン").Range("f4").Value
            s = 10
            e = 15
    End Select
    
    Open ThisWorkbook.Path & "\" & mode & "_" & name & ".vbs" For Output As #1
        Dim i As Long
        For i = s To e
            Print #1, Worksheets("vbscode").Cells(i, 1).Value
        Next
    Close #1
    
    MsgBox "vbsとして出力しました。", vbInformation + vbOKOnly, "完了"
End Sub

SwitchBotAPIのページにかなりわかりやすい解説がありますので、指定された通りにパラメーターを持たせたHTTPリクエストをエンドポイントに送信します。

createHttp関数はリクエストをトークンを使用して組み立てる処理を切り出したものです。各プロシージャから呼んでいます。

getSwitchbotDeviceListプロシージャは機器一覧取得APIをコールします。レスポンスはJSONなので、VBA-JSONモジュールを利用してセルへ展開しています。これに関連して私が大嫌いで普段使わないようにしている参照設定を、やむを得ず「Microsoft Scripting Runtime」に付けています。

execCommandプロシージャはセルに入力されたパラメーターをリクエストに組み立ててコマンドAPIをコールします。テストボタンに割り当てています。このソースコードのbody変数に格納しているような「”」をエスケープしまくり変数入れまくりのstringを作成したい場合はこちらをご利用ください。

exportVBSプロシージャはVBSをメタプログラミングします。しくみは、コマンドジェネレーターシートに入力されたパラメーターを関数でVBSソースコードに組み込んだ文字列をvbscodeシートのセルに表示させています。それを上から順次テキストファイルに書き込み.vbsとして保存しています。VBS出力ボタンから呼んでいます。

私のPCは超高性能なのでExcelでも0.05秒で開ける(誇張)ので、シートに機器操作用ボタンを並べておく方法でもいいのですが、ワンアクションでも短縮したいので個別にVBSに切り出します。

VBSのソースコードはこうなっています。

Dim http
Set http = CreateObject("MSXML2.XMLHTTP")
http.Open "POST", "https://api.switch-bot.com/v1.0/devices/デバイスID/commands", False
http.setRequestHeader "Authorization", "トークン"
http.setRequestHeader "Content-Type", "application/json; charset=utf8"
Dim body
body = "{""command"": ""turnOn"", ""parameter"": ""default"", ""commandType"": ""command""}"
http.Send body

getSwitchbotSceneListexecSceneについては、シーンに関する処理をしているプロシージャで、若干の違いはありますが機器をシーンに置き換えて同じことをしています。

ツールを入手する

利用上のご注意

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

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

switchbot_api_command_generator.zip

おまけ Pythonスクリプトでやる

Pythonスクリプトでも超簡単です。

import json
import requests
from plyer import notification

token = 'トークン'
device_id = 'デバイスID'
title = 'テレビ電源ON'
command = 'turnOn'
cmd_param = 'default'
cmd_type = 'command'
success_msg = 'テレビをONにしました'
url = f'https://api.switch-bot.com/v1.0/devices/{device_id}/commands'
headers = {'Authorization': token, 'Content-Type': 'application/json; charset=utf8'}
data = '{"command": "' + command + '", "parameter": "' + cmd_param + '", "commandType": "' + cmd_type + '"}'

res = requests.post(url, data=data, headers=headers)
result = json.loads(res.text)

if result['statusCode'] == 100:
    msg = success_msg
else:
    msg = '操作が失敗しました'

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

plyerは通知を出すことができるモジュールです。こんな感じになります。VBSで操作の度にメッセージボックスを出したらうるさすぎてイラッときますが、これくらい控えめならいいですね。

まとめ

PCの前から全く動くことなく、リモコンやスマホを手に取ることもなく、TVを消したり、照明をつけたりでき、堕落ライフがますます充実して大変満足です。

SwitchBotのサービスを利用するのに料金はかかりませんが、やっていけるのか心配になります。月額300円とかだったら迷わず払うのでサービス終了にならないでほしいです。

途中に何回も注意事項として書いていますが、トークンの扱いにはくれぐれもご注意ください。今回のようにトークンを複数のファイルに書き込みまくると、うっかりお漏らしするリスクが増えるので、トークンは環境変数にしてしまって、それを参照するコードに書き換えるとよいかもしれません。

おわり。