新版・中華製スマートプラグを node で制御する

[中華製スマートプラグを node で制御する](https://qiita.com/plageoj/items/04c8e5b2de230bf646e3) の記事はそれなりに閲覧があってありがたい限りなのですが、2020年5月に元の Github を見たところ、このセットアップ方法が deprecated になっていました。


そこで、2020年5月時点最新のセットアップ方法を改めて書いておきます。
元のセットアップガイドは [GitHubにあります](https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md)。

# スマートプラグを API で叩きたい

Amazonで [スマートプラグ](https://www.amazon.co.jp/gp/product/B07QKTNFW9) を見つけたので購入してみました。

公式の言うところによると、こいつは Google Home / Alexa / IFTTT との連携が
可能とのこと（IFTTTのみ動作確認しました）。おうちハックがはかどりそうです。

セットアップ後、スマートフォンアプリからはほとんどラグなく操作できるのに、
IFTTT 経由だと最大 10秒近く遅延することがあったので、API で直接叩けたらいいのにな……

と思っていたのですが、ちょいと面倒だったので記事にしておきます。

# Tuya IoT の設定

https://iot.tuya.com にアクセスして、アカウントを作ります。

## プロジェクトの作成
ログインしたら、ページ最上部にある "Cloud Development" を選択。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/58124ebc-7f9d-4dc8-b791-26d1b8141d9f.png)

[Create] ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/cfaf71e7-50bf-0e7e-ac85-d214214b6cf1.png)

ここで適当なプロジェクト名と説明を入力します。industry は何でもいいっぽいですが、`Smart home` にしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/b66216f7-ce6a-7230-32c1-1b1e75c0b441.png)

[create] を押すとプロジェクトが作成されます。この Access ID と Access Secret をメモっておきましょう。

## アプリの作成

次に、ページ最上部の App Service を選択。左のメニューから App SDK を選ぶと、次の画面になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/223995fb-d30d-80c7-7e6b-0d5222625550.png)

Obtain SDK ボタンを押して、Wi-Fi device solution を選択。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/ca9ce12c-dadb-a3b5-4084-ae2df20b137a.png)

Next を押すとアプリの詳細を入力するように言われますが、今回はアプリを作るわけではないので適当でOKです。
アプリ名には短い名前を、次の iOS / Android Bundle name は逆ドメイン形式でアプリパッケージ名を入力します。実在しないものでも大丈夫のようです。

終わったらアプリの詳細が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/22513313-d1a1-8f5b-0c4f-5130cd23942f.png)

Channel ID を控えておきましょう。
node の開発では、iOS Bundle ID と Android Package Name は必要ありません。

## プロジェクトにアプリをひもづけ

Cloud Development ページに戻り、最初に作ったプロジェクトを選択します。
左メニューから Linked Device を選び、タブを Linked devices added ... に切り替えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/33f1f820-801c-eae4-07c9-02f200a6b6c7.png)

Add Apps ボタンを押して、先程作ったアプリを追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/3600c4d4-f21e-932e-4302-85040eaf1d3e.png)

これでクラウド側の設定は完了しました。

# デバイスを追加

すでにデバイスを Tuya Smart アプリ[^1]に追加している場合は一旦削除します。

**2020/5/13 追記**: 後でアプリに追加しなおしたところ、アプリからも node からも操作できるようになりました。
ただし、DHCP の都合で IP アドレスが変わってしまったので、プログラムには変更が必要でした。
アプリ側での登録前に DHCP サーバ側で IP 払い出しを固定しておくとよいかもしれません。

さて、デバイスの接続には以下の要件があります。

+ 2.4GHz 帯の WiFi があること
+ 操作している PC が、接続したい WiFi につながっていること
+ そのネットワークでクライアント間通信ができること
+ 操作している PC に他のネットワークインタフェースがないこと
    + 複数のインタフェースがあると、優先順位によってはパケットがうまく飛びません。

ちなみに、接続作業とその後の開発は別の PC を使っても問題ありません。
ということで、接続作業だけは誰でも持ってる Raspberry Pi Zero W でやるのが安定します。

普段使いのPCで接続作業をされる場合は、WiFi 以外の登録されているインタフェースを全部無効にしておきましょう。

## CLI のインストール

```bash
npm i -g @tuyapi/cli
```

Raspberry Pi で実行したら、権限がないと言われました。
推奨ではないですが、接続作業が終わったらすぐアンインストールするつもりで、安直に sudo をつけて実行しました。

## API との接続

まず、本体を電源に接続し、ボタンを長押ししてペアリングモードにします。ペアリングモードには2種類ありますが、LEDが早く点滅するほうにしてください。

ペアリングモードにしたら、以下のコマンドを実行します。

```bash
tuya-cli link
  --api-key "Access ID"
  --api-secret "Access Secret"
  --schema "Channel ID"
  --ssid "ネットワークのSSID"
  --password "ネットワークのパスワード"
  --region us
```

これで、うまくいくとデバイスIDとローカルキーが表示されるはずです。
あとは、この情報を使って制御することになります。

# API を叩く

ID と key が取得できたら、あとは簡単です。
[codetheweb/tuyapi](https://github.com/codetheweb/tuyapi) をダウンロードしてきて、readme にある通り書けば動きます。

```bash
$ npm i -S codetheweb/tuyapi
```

```javascript:readmeより転載
const TuyAPI = require('tuyapi');

const device = new TuyAPI({
  id: 'xxxxxxxxxxxxxxxxxxxx',
  key: 'xxxxxxxxxxxxxxxx'});

let stateHasChanged = false;

// Find device on network
device.find().then(() => {
  // Connect to device
  device.connect();
});

// Add event listeners
device.on('connected', () => {
  console.log('Connected to device!');
});

device.on('disconnected', () => {
  console.log('Disconnected from device.');
});

device.on('error', error => {
  console.log('Error!', error);
});

device.on('data', data => {
  console.log('Data from device:', data);

  console.log(`Boolean status of default property: ${data.dps['1']}.`);

  // Set default property to opposite
  if (!stateHasChanged) {
    device.set({set: !(data.dps['1'])});

    // Otherwise we'll be stuck in an endless
    // loop of toggling the state.
    stateHasChanged = true;
  }
});

// Disconnect after 10 seconds
setTimeout(() => { device.disconnect(); }, 10000);
```

ちなみに、`on('data')` で取得できる `data` はこんな感じなのですが、

```javascript:data
{
  devId: '42122031cc50e3c3',
  dps: {
    '1': false,
    '9': 0,
    '18': 0,
    '19': 0,
    '20': 1074,
    '21': 1,
    '22': 1428,
    '23': 14342,
    '24': 18691,
    '25': 965
  }
}
```
`dps[1]` がスイッチの状態 (`false` = オフ / `true` = オン) であること以外、
まったくわかりません。おそらく電波強度とかだと思うのですが。

どなたかドキュメントを見つけたら編集リクエストをいただけると助かります。

19/7/25 追記：[node-red-contrib-tuyapi-cloud](https://flows.nodered.org/node/node-red-contrib-tuya-smart) にそれっぽい内容が。



[^1]: 複数の別名アプリが OEM で出ています。Smart Life 等があります。操作方法は変わりません。