2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PCからSwitchBot経由でデスクライトをコントロールする

Posted at

はじめに

ボタンにショートカットなどの操作を自由に割り当てられる、Logicoolの「MX Creative Console」を頂き、色々と試している。
特に「ワンタッチでPCをスリープできる」のはとても便利。

デスクライトも同じようにボタン操作できたらもっと便利になると感じた。
現在デスクライトはSwitchBot社のスマートプラグにつなげてあり、Amazon Echo Show経由で音声操作している。

調べると、SwichBot社が公式API提供していたので、これを使って、スマートプラグのON/OFFをPCから制御できるようにしてみる。

準備

SwitchBotAPIの準備

SwitchBotAPIを使用するのに「トークン」と「クライアントシークレット」が必要。

これらは公式の手順を参考に、SwitchBotアプリから取得する。

  1. SwitchBotアプリを開く -> 「プロフィール」を開く -> 「基本データ」を開く
  2. 「アプリバージョン」欄を10回程度タップする
  3. 「開発者向けオプション」が表示されるので、開く
  4. 「トークン」と「クライアントシークレット」を取得できるので、コピーする

TypeScriptのプロジェクトを作る

勉強も兼ねて、TypeScriptでAPIを操作するバイナリを作ってみる。

参考:https://qiita.com/oharu121/items/3aadafdd5daa8dc53c64

  1. プロジェクトを作成
    npm init -y
  2. Typescriptをインストールする
    npm install typescript ts-node @types/node --save-dev
  3. tsconfig.jsonを作成する
    npx tsc --init
  4. バイナリ化するためのパッケージを追加
    npm install --save-dev pkg
  5. package.jsonにbinとpkgの設定を追加
    "bin": "dist/index.js",
    "pkg": {
        "scripts": "dist/index.js",
        "targets": ["node18-win-x64"],
        "outputPath": "release"
    }
    

[メモ]使い方

  1. src下にtypescriptのコードを用意する(index.ts)
  2. jsコードに変換する
    npx tsc .\src\index.ts --outDir dist
  3. exeファイルに変換する
    npx pkg .
  4. release下にexeファイルが出来るので、それを実行する

[メモ]スクリプトに追加する

  1. package.jsonにスクリプトを追加する
  "scripts": {
    "build": "npx tsc .\\src\\index.ts --outDir dist && npx pkg ."
  },

npm run build でバイナリ生成まで一気にできる

バイナリ作成

Step1. デバイス一覧の取得(動作確認)

まずは、SwitchBotのデバイス一覧が取得できるかを確認する。
SwitchBotAPIのREADMEにJavaScriptのサンプルコードがあるが、これはデバイス操作のコードだったので、以下の記事のコードを使って動かす。

TypeScriptとNode.jsを使ってSwitchBotのDeviceListを確認する(リクエストの署名)

npm install uuid
npm install --save-dev @types/uuid
index.ts
import * as crypto from "crypto";
import * as https from "https";
import { v4 as uuidv4 } from "uuid";

const token: string = "YOUR_TOKEN";
const secret: string = "YOUR_SECRET";
const t = Date.now();
const nonce = uuidv4();
const date = token + t + nonce;
const sign = crypto.createHmac("sha256", secret).update(date).digest("base64");

const host: string = "api.switch-bot.com";
const path: string = "/v1.1/devices";

const postOptions: https.RequestOptions = {
  host: host,
  path: path,
  method: "GET",
  port: 443,
  headers: {
    "content-type": "application/json",
    authorization: token,
    sign: sign,
    t: t,
    nonce: nonce,
  },
};

function getDevices(getOptions: https.RequestOptions): Promise<any> {
  return new Promise((resolve, reject) => {
    const req = https.request(getOptions, (res) => {
      let queue: Buffer[] = [];
      res.on("data", (chunk) => {
        queue.push(chunk);
      });
      res.on("end", () => {
        const data = Buffer.concat(queue);
        resolve(data.toString());
      });
    });
    req.on("error", (e) => {
      console.log(`request error: ${JSON.stringify(e)}`);
      reject(e);
    });
    req.end();
  });
}

(async () => {
  try {
    const result = await getDevices(postOptions);
    JSON.parse(result).body.deviceList.forEach((devices: any) => {
      console.log(`devices:${JSON.stringify(devices)}\n`);
    });
    JSON.parse(result).body.infraredRemoteList.forEach(
      (infraredRemote: any) => {
        console.log(`infraredRemote:${JSON.stringify(infraredRemote)}\n`);
      }
    );
  } catch (e) {
    console.log(`error:${JSON.stringify(e)}`);
  }
})();

Step2. プラグをON/OFFする

デスクライトは、SwitchBotのスマートプラグに繋いでいる。
スマートプラグはSwitchBotAPIのdevices/{deviceId}/commandsにコマンドを送ることで操作できる。
deviceIdはStep1で取得したdeviceListで確認できる。

SwitchBot公式ドキュメントによると、スマートプラグの操作は以下。

  • commandturnOnを指定するとプラグがON(点灯)
  • commandturnOffを指定するとプラグがOFF(消灯)
index.ts
import * as crypto from "crypto";
import * as https from "https";
import { v4 as uuidv4 } from "uuid";

const token: string = "YOUR_TOKEN";
const secret: string = "YOUR_SECRET";
const deviceId: string = "YOUR_DEVICEID";
const t = Date.now();
const nonce = uuidv4();
const date = token + t + nonce;
const sign = crypto.createHmac("sha256", secret).update(date).digest("base64");

const host: string = "api.switch-bot.com";
const path: string = `/v1.1/devices/${deviceId}/commands`;

const body = {
  command: "turnOn", // turnOnとturnOff
  parameter: "default",
  commandType: "command",
};

const postOptions: https.RequestOptions = {
  host: host,
  path: path,
  method: "POST",
  port: 443,
  headers: {
    "content-type": "application/json",
    authorization: token,
    sign: sign,
    t: t,
    nonce: nonce,
  },
};

function controlDevice(
  options: https.RequestOptions,
  body: object
): Promise<void> {
  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      res.on("end", () => {
        resolve();
      });
      res.resume();
    });

    req.on("error", (e) => {
      console.log(`request error: ${JSON.stringify(e)}`);
      reject(e);
    });

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

(async () => {
  try {
    await controlDevice(postOptions, body);
    console.log(`command sent successfully.`);
  } catch (e) {
    console.log(`error:${JSON.stringify(e)}`);
  }
})();

Step3. プラグの状態を取得する

Step1で利用している全デバイスの一覧を取得したが、deviceIdを指定することで、そのデバイス単体の状態を取得することができる。
/v1.1/devices/{deviceId}/status
スマートプラグの現在の状態(ON/OFF)もpowerに入っている。

index.ts
import * as crypto from "crypto";
import * as https from "https";
import { v4 as uuidv4 } from "uuid";

const token: string = "YOUR_TOKEN";
const secret: string = "YOUR_SECRET";
const deviceId: string = "YOUR_DEVICEID";
const t = Date.now();
const nonce = uuidv4();
const date = token + t + nonce;
const sign = crypto.createHmac("sha256", secret).update(date).digest("base64");

const host: string = "api.switch-bot.com";
const path: string = `/v1.1/devices/${deviceId}/status`;

const postOptions: https.RequestOptions = {
  host: host,
  path: path,
  method: "GET",
  port: 443,
  headers: {
    "content-type": "application/json",
    authorization: token,
    sign: sign,
    t: t,
    nonce: nonce,
  },
};

function getDevices(getOptions: https.RequestOptions): Promise<any> {
  return new Promise((resolve, reject) => {
    const req = https.request(getOptions, (res) => {
      let queue: Buffer[] = [];
      res.on("data", (chunk) => {
        queue.push(chunk);
      });
      res.on("end", () => {
        const data = Buffer.concat(queue);
        resolve(data.toString());
      });
    });
    req.on("error", (e) => {
      console.log(`request error: ${JSON.stringify(e)}`);
      reject(e);
    });
    req.end();
  });
}

(async () => {
  try {
    const result = await getDevices(postOptions);
    const json = JSON.parse(result);
    console.log(`deviceInfo: ${JSON.stringify(json)}`);
    console.log(`status: ${json.body.power}`);
  } catch (e) {
    console.log(`error:${JSON.stringify(e)}`);
  }
})();

応答結果

deviceInfo: {"statusCode":100,"body":{"version":"V2.0","power":"on","deviceId":"*****","deviceType":"Plug","hubDeviceId":"*****"},"message":"success"}
status: on

statusonなら点灯、offなら消灯状態だった。

Step4. プラグの状態に応じて動作を切り替える(トグル制御)

デスクライトのON・OFF用にそれぞれ別々のバイナリを作り、ボタン2つに割り当てる方法もあるが、
プラグの状態をGETして、「ON」なら「OFF」に、「OFF」なら「ON」に切り替えるコードを書き、ボタン1つでデスクライトの操作が完結できるようにする。

index.ts
import * as crypto from "crypto";
import * as https from "https";
import { v4 as uuidv4 } from "uuid";

// SwitchBot 認証情報
const token: string = "YOUR_TOKEN";
const secret: string = "YOUR_SECRET";
const deviceId: string = "YOUR_DEVICEID";
const host: string = "api.switch-bot.com";
const path: string = `/v1.1/devices/${deviceId}/status`;

// ヘッダー生成
function buildAuthHeaders() {
  const t = Date.now();
  const nonce = uuidv4();
  const date = token + t + nonce;
  const sign = crypto
    .createHmac("sha256", secret)
    .update(date)
    .digest("base64");

  return {
    t,
    nonce,
    sign,
    headers: {
      "content-type": "application/json",
      authorization: token,
      sign: sign,
      t: t,
      nonce: nonce,
    },
  };
}

// プラグのステータスを取得する関数
function getPlugStatus(): Promise<"on" | "off"> {
  return new Promise((resolve, reject) => {
    const { headers } = buildAuthHeaders();
    const options: https.RequestOptions = {
      host: host,
      path: `/v1.1/devices/${deviceId}/status`,
      method: "GET",
      port: 443,
      headers: headers,
    };

    const req = https.request(options, (res) => {
      let queue: Buffer[] = [];
      res.on("data", (chunk) => queue.push(chunk));
      res.on("end", () => {
        const result = JSON.parse(Buffer.concat(queue).toString());
        const power: "on" | "off" = result.body.power;
        resolve(power);
      });
    });

    req.on("error", (e) => reject(e));
    req.end();
  });
}

// プラグを操作する関数
function controlPlug(command: "turnOn" | "turnOff"): Promise<void> {
  return new Promise((resolve, reject) => {
    const { headers } = buildAuthHeaders();
    const body = JSON.stringify({
      command: command,
      parameter: "default",
      commandType: "command",
    });

    const options: https.RequestOptions = {
      host: host,
      path: `/v1.1/devices/${deviceId}/commands`,
      method: "POST",
      port: 443,
      headers: {
        ...headers,
      },
    };

    const req = https.request(options, (res) => {
      res.on("data", () => {});
      res.on("end", () => resolve());
    });

    req.on("error", (e) => reject(e));
    req.write(body);
    req.end();
  });
}

const postOptions: https.RequestOptions = {
  host: host,
  path: path,
  method: "GET",
  port: 443,
  headers: {},
};

(async () => {
  try {
    const status = await getPlugStatus();
    console.log(`status: ${status}`);

    const command = status === "on" ? "turnOff" : "turnOn";
    console.log(`command: ${command}`);

    await controlPlug(command);
    console.log("command sent successfully.");
  } catch (e) {
    console.log(`error:${JSON.stringify(e)}`);
  }
})();

MX Creative Consoleでバイナリを動かす

Logi Options+で、作成したバイナリを実行するボタンを用意する。

  1. 「システムアクション」->「アドバンス」->「実行」をボタンに割り当てる
  2. 実行するプログラムに、生成したバイナリを指定する

これで、ボタンを押すだけで、SwitchBotのスマートプラグ=デスクライトのON/OFFを操作できるようになった。

参考

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?