VSCodeでSwitchBot APIを操作するマクロの作り方

VSCode Macros拡張を使ってSwitchBot APIを操作するマクロを作成してみたので紹介します。

今回はXの界隈で流行りのCursor Composerを使ってマクロを組んでみましたが、一発で作れてしまった…。Cursorエディタ便利すぎますね。


事前準備

  1. SwitchBotのAPIトークンとシークレットキーの取得

    以下の公式サイトの方法でAPIトークンとシークレットキーを取得してください。

    SwitchBotサポート
    トークンの取得方法

  2. ライブラリのインストール ※ハードコードする場合はスキップできます。

    環境変数をdotenvを使ってマクロファイルフォルダ内の.envから取得しているため、

    npm i dotenv

    でマクロフォルダにインストールしてください。

  3. APIトークンとシークレットキーを環境変数に登録

    マクロディレクトリに.envファイルを作成し、以下の環境変数に先程取得したAPIトークンとシークレットキーを登録します。

    環境変数名
    API_TOKEN APIトークン
    API_SECRET シークレットキー


サンプルマクロ

SwitchBot.js

const vscode = require('vscode');
const process = require('process');
const crypto = require('crypto');
const https = require('https');
const { Buffer } = require('buffer');
const path = require('path');

// マクロディレクトリから.envファイルを読み込む
require('dotenv').config({ path: path.join(__dirname, '.env') });

const token = process.env.API_TOKEN;
const secret = process.env.API_SECRET;
const lightDeviceId1 = process.env.LIGHT_DEVICE_ID_1;

// 環境変数のチェック
if (!token || !secret || !lightDeviceId1) {
  vscode.window.showErrorMessage('環境変数が設定されていません。.env ファイルを確認してください。');
  process.exit(1);
}

module.exports.macroCommands = {
  電気を消す: {
    no: 1,
    func: TurnOffLight,
  },
  電気をつける: {
    no: 2,
    func: TurnOnLight,
  },
  デバイス情報取得: {
    no: 3,
    func: getDevices,
  },
};

// 電気を消すコマンド
async function TurnOffLight() {
  try {
    await sendCommand('turnOff', lightDeviceId1);
    await vscode.window.showInformationMessage('電気を消しました');
  } catch (error) {
    await vscode.window.showErrorMessage('電気を消すコマンドの実行中にエラーが発生しました:', error);
  }
}

// 電気をつけるコマンド
async function TurnOnLight() {
  try {
    await sendCommand('turnOn', lightDeviceId1);
    await vscode.window.showInformationMessage('電気を点けました');
  } catch (error) {
    await vscode.window.showErrorMessage('電気を点けるコマンドの実行中にエラーが発生しました:', error);
  }
}

// デバイスID取得関数
async function getDevices() {
  try {
    const deviceInfo = await getDeviceInfo();
    const newEditor = await vscode.workspace.openTextDocument({ content: JSON.stringify(deviceInfo, null, 2), language: 'json' });
    await vscode.window.showTextDocument(newEditor);
    return deviceInfo;
  } catch (error) {
    await vscode.window.showErrorMessage(`デバイス情報の取得中にエラーが発生しました: ${error.message}`);
    throw error;
  }
}

// コマンド送信関数
async function sendCommand(command, deviceId) {
  const t = Date.now();
  const nonce = 'requestID';
  const data = token + t + nonce;
  const signTerm = crypto.createHmac('sha256', secret).update(Buffer.from(data, 'utf-8')).digest();
  const sign = signTerm.toString('base64');

  const body = JSON.stringify({
    command: command,
    parameter: 'default',
    commandType: 'command',
  });

  const options = {
    hostname: 'api.switch-bot.com',
    port: 443,
    path: `/v1.1/devices/${deviceId}/commands`,
    method: 'POST',
    headers: {
      Authorization: token,
      sign: sign,
      nonce: nonce,
      t: t,
      'Content-Type': 'application/json',
      'Content-Length': body.length,
    },
  };

  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      let data = '';
      res.on('data', (chunk) => {
        data += chunk;
      });
      res.on('end', () => {
        vscode.window.showInformationMessage(`ステータスコード: ${res.statusCode}`);
        vscode.window.showInformationMessage(data);
        resolve(data);
      });
    });

    req.on('error', (error) => {
      vscode.window.showErrorMessage(`エラー: ${error.message}`);
      reject(error);
    });

    req.write(body);
    req.end();
  });
}

// デバイス情報取得関数
async function getDeviceInfo() {
  const path = '/v1.1/devices';
  const nonce = crypto.randomUUID(); // Node.jsのcryptoモジュールを使用してUUIDを生成
  const timestamp = Date.now().toString();

  const data = token + timestamp + nonce;
  const signTerm = crypto.createHmac('sha256', secret).update(Buffer.from(data, 'utf-8')).digest();
  const sign = signTerm.toString('base64');

  const options = {
    hostname: 'api.switch-bot.com',
    port: 443,
    path: path,
    method: 'GET',
    headers: {
      Authorization: token,
      sign: sign,
      t: timestamp,
      nonce: nonce,
    },
  };

  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      let data = '';
      res.on('data', (chunk) => {
        data += chunk;
      });
      res.on('end', () => {
        vscode.window.showInformationMessage(`ステータスコード: ${res.statusCode}`);
        try {
          const parsedData = JSON.parse(data);
          vscode.window.showInformationMessage('デバイス情報を取得しました');
          resolve(parsedData);
        } catch (error) {
          vscode.window.showErrorMessage(`レスポンスの解析中にエラーが発生しました: ${error.message}`);
          reject(error);
        }
      });
    });

    req.on('error', (error) => {
      vscode.window.showErrorMessage(`エラー: ${error.message}`);
      reject(error);
    });

    req.end();
  });
}


使い方

ここからはVSCode Macrosのコマンド(VSCMacros: Select a macro fileSwitchBot.jsVSCMacros: Run a macro)で操作します。

file

  1. デバイスIDの取得

    VSCode Macrosのコマンドからデバイス情報取得コマンドを実行すると、新しいテキストエディタが開いて利用中のSwitchBotデバイス一覧がJson形式で表示されるので、制御したいデバイスのデバイスIDを見つけて.envに登録します(本例の場合はライトなのでLIGHT_DEVICE_ID_1)。

    file

  2. デバイスの制御

    後は想像通りですが、VSCode Macrosのコマンドから電気を点けるを実行すればライトが点灯し、電気を消すコマンドを実行すればライトが消灯します。

SwitchBot APIの詳しい使い方については、参考ウェブサイトのドキュメント参照してみてください。


参考ウェブサイトなど

  • GitHub
    OpenWonderLabs/SwitchBotAPI


以上です。

シェアする

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

フォローする