私の部屋の家電製品の大半は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です。
うまく動作しない場合は
commandType | Command | command parameter |
command | turnOn | default |
はすべての機器に適用できる(たぶん)ので、この設定で電源をONにすることができるか確認してください。
8.VBS出力ボタンを押します。
Excelと同じフォルダに.vbsファイルが出力されます。vbsにはトークンがそのまま書き込まれているので第三者に公開しないように注意してください。このファイルを開くとAPIがコールされ操作が実行されます。
アイコンをそれらしくすれば完成です。SwitchBotのロゴ画像は配布されていなそうなので、ぽいのを自作するか、基本自分だけが使うのでどこかからコピってきましょう。ちなみに下の画像のは私がフォトショで40秒で作成したアイコンです。
シーンを実行したい場合は「シーン」シートのシーンリスト取得ボタンでシーン一覧を取得して対象のシーンのIDをsceneIDセルに入力します。
シーンのテストと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
getSwitchbotSceneList、execSceneについては、シーンに関する処理をしているプロシージャで、若干の違いはありますが機器をシーンに置き換えて同じことをしています。
ツールを入手する
おまけ 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円とかだったら迷わず払うのでサービス終了にならないでほしいです。
途中に何回も注意事項として書いていますが、トークンの扱いにはくれぐれもご注意ください。今回のようにトークンを複数のファイルに書き込みまくると、うっかりお漏らしするリスクが増えるので、トークンは環境変数にしてしまって、それを参照するコードに書き換えるとよいかもしれません。
おわり。