ものづくり

【ついに自動換気システム完成!】SwitchBotプラグミニをつかってGAS(Google Apps Script)からUSBファンを制御する

thumbnail
n-mukineer
えぬ
えぬ

こんにちは、えぬ(@nmukineer)です!

SwichBotプラグミニを使ってUSBファンをWiFi経由でON/OFFできるようにしてみました!今回で自動換気システムの製作が完了です!

前回の記事
【簡単グラフ化】GASからAmbientにデータを送信してCO2濃度を可視化する
【簡単グラフ化】GASからAmbientにデータを送信してCO2濃度を可視化する

SwitchBotとは?

SwitchBotシリーズ

SwitchBot(スイッチボット)」はスイッチやボタンをON/OFFすることができるIoTデバイスです。外出先からエアコンをつけたり、決められた時間に電気をつけたり消したりするようなこと(いろんなシリーズがあるので、多種多様な使い方があります)ができる、超便利なガジェットです。

SwitchBotプラグミニについて

SwitchBotプラグミニ」は、コンセント(AC100Vが通っている)部分を、WiFi経由でON/OFFすることができるSwitchBotです。

照明や扇風機のような生活家電を接続しておき、音声操作やスマホ操作によってON/OFFすることでスマートライフを実現できます。

今回は、この「SwitchBotプラグミニ」を実際に購入して、M5Stackで測定したCO2濃度に連動させてUSBファンをON/OFFするプログラムをGAS(Google Apps Script)で作ってみました。

SwichBot APIをGASから制御してみる

えぬ
えぬ

SwitchBotはAPIを使うことができるため自分で作ったプログラムからSwitchBotを制御することができます!

APIトークンの取得方法

SwitchBot APIのAPIトークンは非常にわかりづらいですが、答えはGithubにあります!

簡単に手順を説明しますと、

  1. スマホアプリをダウンロードする
  2. スマホアプリ上で「アプリバージョン」部分を10回タップする

こうすることで「開発者向けオプション」が現れ、クリックすることでトークン画面を開くことができます!

SwitchBotアプリ
アプリバージョンを10回タップすると開発者向けオプションが表示される!これはGithub見ずにわかるはずがないです笑

GASのスクリプトプロパティにAPIトークンを設定

スクリプトを書いていく前に、GASのスクリプトプロパティにSwitchBot APIのトークンとシークレット、および、使用するSwitchBotのデバイスID(※)を登録しておきます。(スクリプトプロパティへの登録方法については以下の記事も参照してください)

あわせて読みたい
GASで作ったAPIにM5StackからPOSTする方法
GASで作ったAPIにM5StackからPOSTする方法

※デバイスIDの取得方法については次の章を参照してください

GASのスクリプトプロパティにAPI情報とデバイス情報を追加しておく

SwitchBotのデバイスID取得方法

順番が前後しますが、GASを使ってSwitchBotのデバイスIDを取得する方法を書いておきます。

SwitchBotアプリでSwitchBotをWiFiに接続します。

GASで以下のスクリプトを実行します。

const SWITCHBOT_API_TOKEN = PropertiesService.getScriptProperties().getProperty('SWITCHBOT_API_TOKEN');
const SWITCHBOT_API_SECRET = PropertiesService.getScriptProperties().getProperty('SWITCHBOT_API_SECRET');
const SWITCHBOT_API_HOST = "https://api.switch-bot.com";
const SWITCHBOT_API_HEADERS = {
  'Authorization': SWITCHBOT_API_TOKEN,
  'Content-Type': 'application/json; charset=utf8'
}

function getDeviceIds() {
  const url = `${SWITCHBOT_API_HOST}/v1.0/devices`;
  const options = {
    'headers': SWITCHBOT_API_HEADERS
  }
  const res = UrlFetchApp.fetch(url, options);
  const json = JSON.parse(res.getContentText());
  console.log(json['body']['deviceList']);
}

実行すると、実行ログ画面に以下のようにdeviceIdが現れますので、メモっておきましょう。

GASでSwitchBotのデバイスIDを取得した結果

自動換気システムのスクリプト遂に完成!

えぬ
えぬ

これまでコツコツ作ってきた自動換気システムのスクリプトが完成します!

SwitchBotをコントロールするメソッド(controlSwitchBot)を作成

まずはSwitchBotを制御するメソッドを書きます。引数にコマンド(「turnOn」または「turnOff」という文字列にします)を指定し、その通りにSwitchBotを操作するものとなります。

const SWITCHBOT_API_TOKEN = PropertiesService.getScriptProperties().getProperty('SWITCHBOT_API_TOKEN');
const SWITCHBOT_API_HOST = "https://api.switch-bot.com";
const SWITCHBOT_API_HEADERS = {
  'Authorization': SWITCHBOT_API_TOKEN,
  'Content-Type': 'application/json; charset=utf8'
}

const SWITCHBOT_DEVICE_ID = PropertiesService.getScriptProperties().getProperty('SWITCHBOT_DEVICE_ID');
function controlSwitchBot(command) {
  const url = `${SWITCHBOT_API_HOST}/v1.0/devices/${SWITCHBOT_DEVICE_ID}/commands`;
  const data = {
    'command': `${command}`,
    'parameter': 'default',
    'commandType': 'command'
  }
  const options = {
    'method': 'post',
    'headers': SWITCHBOT_API_HEADERS,
    'payload': JSON.stringify(data)
  }
  const response = UrlFetchApp.fetch(url, options);
  const json = JSON.parse(response.getContentText());
  console.log(json);
}

SwitchBotを制御するメソッドを使いやすくする

今回、ONまたはOFFの二値制御ですので、controlSwitchBotメソッドをラップして使いやすくしておきます。

const COMMANDS = {
  'turnOn': 'turnOn',
  'turnOff': 'turnOff'
}

function turnOn() {
  controlSwitchBot(COMMANDS.turnOn);
}

function turnOff() {
  controlSwitchBot(COMMANDS.turnOff);
}

このようにしておくことで、

  • ONするとき:turnOn()
  • OFFするとき:turnOff()

と1行書けばSwitchBotを制御できます!

テスト用メソッドを書く

testControlSwitchBotというテスト用メソッドを書いてみます。

function testControlSwitchBot() {
  turnOn();
  Utilities.sleep(2000);
  lineNotify('2秒ONします');
  PropertiesService.getScriptProperties().setProperty("VENTILATION", "ON");
  Logger.log(PropertiesService.getScriptProperties().getProperty("VENTILATION"));
  turnOn();
  Utilities.sleep(2000);
  turnOff();
  lineNotify('OFFします');
  PropertiesService.getScriptProperties().setProperty("VENTILATION", "OFF");
  Logger.log(PropertiesService.getScriptProperties().getProperty("VENTILATION"));
}

VENTILATIONというスクリプトプロパティを利用して、現在SwitchBotがONなのかOFFなのかを管理するようにしています。

動きは「2秒ONしたあと、OFFする」という単純なものです。このテストメソッドにより、

  • SwitchBotとうまく通信できているか
  • ON/OFFの動きに問題がないか
  • スクリプトプロパティの書き込み、読み出しに問題ないか

ということをテストすることができます。

実行ログは以下です。

testControlSwitchBotメソッドの実行ログ

lineNotifyメソッドの正体については、以下の記事に書いてますのでご参照下さい。

あわせて読みたい
GASからLINE Notifyを使ってLINE通知する方法
GASからLINE Notifyを使ってLINE通知する方法

CO2センサからデータを受信したときにSwitchBotを制御する

さて、いよいよCO2センサとの連携部分を書いていきます!

…と言っても、ほとんど必要な部品は出来上がっているので、あとはそれを組み合わせてロジックを作っていくだけです。

実装する前に、実装したいロジックについて簡単に説明しておきます。

  • 1000ppm以上になったら、SwitchBotをONする
  • 600ppm以下になったら、SwitchBotをOFFする

これだけです!図にするとこちらのようになります。

SwitchBotの制御特性

行きと帰りの閾値を別々にすることによって、「ヒステリシス」的な特性になっていますね。

ということで、上記のロジックを実装したものが以下のGASコードとなります。(前回までのコードも含んだものになります)

const SWITCHBOT_API_TOKEN = PropertiesService.getScriptProperties().getProperty('SWITCHBOT_API_TOKEN');
const SWITCHBOT_API_HOST = "https://api.switch-bot.com";
const SWITCHBOT_API_HEADERS = {
  'Authorization': SWITCHBOT_API_TOKEN,
  'Content-Type': 'application/json; charset=utf8'
}
const SWITCHBOT_DEVICE_ID = PropertiesService.getScriptProperties().getProperty('SWITCHBOT_DEVICE_ID');
const COMMANDS = {
  'turnOn': 'turnOn',
  'turnOff': 'turnOff'
}
const LINE_NOTIFY_API = 'https://notify-api.line.me/api/notify';
const LINE_NOTIFY_TOKEN = PropertiesService.getScriptProperties().getProperty('LINE_NOTIFY_TOKEN');
const AMBIENT_CHANNEL_ID = PropertiesService.getScriptProperties().getProperty('AMBIENT_CHANNEL_ID');
const AMBIENT_WRITE_KEY = PropertiesService.getScriptProperties().getProperty('AMBIENT_WRITE_KEY');
const AMBIENT_API = `http://ambidata.io/api/v2/channels/${AMBIENT_CHANNEL_ID}/data`;
const CO2_THRESHOLDS = {
  'lineNotify': 1000,
  'turnOn': 1000,
  'turnOff': 600
};
const LINE_NOTIFY_TEXT = `【集中力低下注意!】CO2濃度が${CO2_THRESHOLDS.lineNotify}ppmを超えました。今すぐ換気しましょう!`;

function doPost(e) {
  const body = JSON.parse(e.postData.contents) 
  const co2 = body.co2;
  const temp = body.temp;
  console.log(`CO2濃度: ${co2}ppm`);
  console.log(`温度:${temp}℃`)

  if(co2 >= CO2_THRESHOLDS.lineNotify) {
    lineNotify(LINE_NOTIFY_TEXT + `現在のCO2濃度は${co2}ppmです。`);
  }
  ambientSend(co2, temp);
  
  if (co2 >= CO2_THRESHOLDS.turnOn) {
    setVentilationOn();
  } else if (co2 <= CO2_THRESHOLDS.turnOff) {
    setVentilationOff();
  }
}

function setVentilationOn() {
  turnOn();

  if (PropertiesService.getScriptProperties().getProperty("VENTILATION") == 'OFF') {
    lineNotify(`CO2濃度が${CO2_THRESHOLDS.turnOn}ppmを上回ったため自動換気システムが作動しました`);
    PropertiesService.getScriptProperties().setProperty("VENTILATION", "ON");
  }
}

function setVentilationOff() {
  turnOff();
  
  if (PropertiesService.getScriptProperties().getProperty("VENTILATION") == 'ON') {
    lineNotify(`CO2濃度が${CO2_THRESHOLDS.turnOff}ppmを下回ったため自動換気システムが停止しました`);
    PropertiesService.getScriptProperties().setProperty("VENTILATION", "OFF");
  }
}

function lineNotify(text) {
  const options = {
    "method" : "post",
    "payload": {"message": text},
    "headers": {"Authorization":"Bearer " + LINE_NOTIFY_TOKEN}
   }
  UrlFetchApp.fetch(LINE_NOTIFY_API, options)
}

function ambientSend(co2, temp) {
  const data = {
    "writeKey": AMBIENT_WRITE_KEY,
    "d1": Number(co2),
    "d2": Number(temp)
  }
  const options = {
    "method" : "post",
    "contentType": "application/json",
    "payload": JSON.stringify(data)
  }
  UrlFetchApp.fetch(AMBIENT_API, options)
}

function testLineNotify() {
  lineNotify('テスト通知じゃあああああああああああい!')
}

function testAmbientSend() {
  ambientSend(500, 23);
}

function testControlSwitchBot() {
  lineNotify('2秒ONします');
  PropertiesService.getScriptProperties().setProperty("VENTILATION", "ON");
  Logger.log(PropertiesService.getScriptProperties().getProperty("VENTILATION"));
  turnOn();
  Utilities.sleep(2000);
  turnOff();
  lineNotify('OFFします');
  PropertiesService.getScriptProperties().setProperty("VENTILATION", "OFF");
  Logger.log(PropertiesService.getScriptProperties().getProperty("VENTILATION"));
}

function turnOn() {
  controlSwitchBot(COMMANDS.turnOn);
}

function turnOff() {
  controlSwitchBot(COMMANDS.turnOff);
}

function controlSwitchBot(command) {
  const url = `${SWITCHBOT_API_HOST}/v1.0/devices/${SWITCHBOT_DEVICE_ID}/commands`;
  const data = {
    'command': `${command}`,
    'parameter': 'default',
    'commandType': 'command'
  }
  const options = {
    'method': 'post',
    'headers': SWITCHBOT_API_HEADERS,
    'payload': JSON.stringify(data)
  }
  const response = UrlFetchApp.fetch(url, options);
  const json = JSON.parse(response.getContentText());
  console.log(json);
}

function getDeviceIds() {
  const url = `${SWITCHBOT_API_HOST}/v1.0/devices`;
  const options = {
    'headers': SWITCHBOT_API_HEADERS
  }
  const res = UrlFetchApp.fetch(url, options);
  const json = JSON.parse(res.getContentText());
  console.log(json['body']['deviceList']);
}

デプロイ・動作確認

先ほどのスクリプトをデプロイして、動作確認してみましょう!(デプロイ方法については以下の記事を参照)

あわせて読みたい
GASからLINE Notifyを使ってLINE通知する方法
GASからLINE Notifyを使ってLINE通知する方法

今回は、M5StackからのPOSTを待つのではなく、M5Stackを模擬した信号をPCから送ってみます。

curlコマンドが使える環境を用意し、下記のコマンドを送信します。(「デプロイID」の部分に自分のGASのデプロイIDを入れて下さい。)

curl -i -X POST 'https://script.google.com/macros/s/デプロイID/exec' -H 'Content-Type:application/json' -d '{"co2":"1000"}'

うまく動きましたか?

動いた場合はLINE通知が来て、SwitchBotがONになります。

次に、SwitchBotをOFFにするコマンドを送ってみます。CO2濃度が600以下だと停止するはずなので、CO2の値を「600」に設定して送ります。

curl -i -X POST 'https://script.google.com/macros/s/デプロイID/exec' -H 'Content-Type:application/json' -d '{"co2":"600"}'

USBファンを接続し、自動換気システムの完成!

SwitchBotの制御もできるようになったので、あとはUSBファンを接続し、ファンを壁の通気口に向けます。

筆者の自宅はマンションなのですが、部屋にこのような通気口がついています。

マンションの通気口
マンションの通気口

これに…USBファンを、くっつけます!(ガムテープ!!!)

USBファンを通気口に着けた
USBファンを通気口に着けてみた

USBファンはこういったものを買ってます。

ということで、完成しました!

実際の換気能力は今後検証していきます!

自動換気システムの全貌はこちらです。

自動換気システム構成図

まとめ

SwitchBotプラグミニを使用して、CO2センサから受けたデータを基にしてGASからUSBファンの動きを制御するプログラムを作ってみました。

ハードウェア、ファームウェア、Web APIと、一連のIoTプロトタイピングを学ぶことができるので、トライしてみてはいかがでしょうか?

ここまでお読みいただきありがとうございました!

このブログを書いている人
えぬ
えぬ
N日後にムキムキになるエンジニア
WebアプリエンジニアとしてIoTシステムを開発中。30代折り返し。 趣味(モノづくり、プログラミング、筋トレ)や子育てのことを主に記事にします。 TOEIC: 900点/第一級陸上無線技術士/第3種電気主任技術者/技術士一次試験合格/基本情報技術者/第2種電気工事士/デジタル技術検定2級(情報・制御)
記事URLをコピーしました