今日から私も obnizer ！ドリンクディスペンサーを IoT 化してみた。(obniz編)

こんにちは。野良ハックチーム ざっきーと申します。
IoTLT みっつめ！ Advent Calendar 2018の 14日目を担当します。

<B>ようやく私も <A HREF="https://obniz.io/">obniz</A> を買いました！ (遅っ！</B>
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/696b76fd-368c-31b9-dfe6-9933c3c27ffd.png" width="50%">

フツーは Lチカ から始めるものですが、野良ハックメンバーが LED を爆発させていたので、恐れてサーボモーターを動かすことにしました。公式のチュートリアル (<A HREF="https://obniz.io/doc/lessons_turning_on_led">obniz 入門レッスン</A>) にはちゃんと注意が書かれていましたので、みなさんご注意を！
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/11441733-b269-ef5e-6a3a-ffdb5f4fe59f.png" width="50%">
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/f026d416-f253-bb57-8a19-466f0d247be4.png" width="30%"> <img src="https://qiita-image-store.s3.amazonaws.com/0/72479/ece3fb37-166d-38c5-be02-09b5f9c4fe83.png" width="50%">

## ドリンクディスペンサーの IoT 化
野良ハックチームでサーボモーターを使っている作品？！と言えば<A HREF="https://qiita.com/kitazaki/items/2dba65fced6a16684969">ドリンクディスペンサー</A> (サングリアディスペンサー) ということで、過去 <A HREF="https://iotlt.connpass.com/event/85156/">SIerIoTLT vol.8</A> や <A HREF="https://iotlt.connpass.com/event/89493/">SIerIoTLT vol.9</A> でお披露目しています。さっそく obniz へ移植することに決めました。
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/99398335-931a-854a-03c4-29d462c3c0df.jpeg" width="45%"> <img src="https://qiita-image-store.s3.amazonaws.com/0/72479/ff95132f-0271-1f81-70a1-5d700f8b8900.jpeg" width="45%">

## 構成・配線
### 移植前
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/d21a5c8b-b1c4-9500-2709-57566d3930e1.png">
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/40e4a6cf-5c5f-00de-b4a3-96d0f9af2ee0.png" width="50%">

### 移植後
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/9036d643-d218-0353-95b0-c2904a31e4f8.png">
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/da81b111-e644-2ba3-88b2-e27d52cd03de.png" width="50%">

### LINE Clova
スマートスピーカー (今回は LINE Clova を使用) の変更点は Endpoint (サーバー設定
 → Extension サーバーの URL) です。ここには後述の RunKit で生成された URL を入力します。今回はアプリ起動時にサーボモーターも動かすようにしたので、対話モデルの作成は不要です。(´・ω・`)
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/2225122a-a80e-27b1-260e-a934ddb46ed8.png">

### RunKit
enebular (Node-RED) の部分は <A HREF="https://runkit.com">RunKit</A> で作成します。obniz の中級者向けレッスン (<A HREF="https://obniz.io/doc/lessons_nodejs">Nodejs で使う</A>) に記載されています。
また、2018 年 8 月 29 日に開催された「<A HREF="https://linedev.connpass.com/event/96795/">obniz × LINE BOOT AWARDS 夏の終わりの駆け込みハンズオン</A>」イベントの<A HREF="https://object-storage.tyo1.conoha.io/v1/nc_35a49f4e51c74e35ad3493c52d37f63e/images/LINE%20x%20obniz%20%E3%83%8F%E3%83%B3%E3%82%BA%E3%82%AA%E3%83%B3.pdf">ハンズオン資料</A>を参考にしても良いと思います。
ソースコードの中で <B>YOUR_CLOVA_EXTENSION_ID</B> と <B>YOUR_OBNIZ_ID</B> だけ自分の情報を入力する必要があります。

### ソースコード

```javascript:sangria_dispenser.js
const Obniz = require('obniz');
const express = require('@runkit/runkit/express-endpoint/1.0.0');
const clova = require('@line/clova-cek-sdk-nodejs');
const app = express(exports);

const clovaExtensionId = 'YOUR_CLOVA_EXTENSION_ID'; // Clova Extension ID を入力する
const yourObnizId = "YOUR_OBNIZ_ID"; // obniz ID を入力する

// 使い方のテンプレート
const TEMPLATE_INQUIRY = 'サングリアをどうぞ';

//obnizを動かす部分
async function dispenser() {
  let obniz = new Obniz(yourObnizId);
  let connected = await obniz.connectWait({ timeout: 5 });
  if (!connected) {
    // 5秒探してobnizが見つからない（オフラインの場合）は何もしない
    return false;
  }
  if(connected){
    var servo = obniz.wired("ServoMotor", {signal:2,vcc:1, gnd:0});
    obniz.display.clear();
    obniz.display.print("ServoMotor: 100")
    servo.angle(100.0);
    await obniz.wait(4000);
    obniz.display.clear();
    obniz.display.print("ServoMotor: 0")
    servo.angle(0.0);
    await obniz.wait(1000);
    }
  // コネクションを切断する
  obniz.close();
  return true;
}

//clovaとの連携部分
const clovaSkillHandler = clova.Client.configureSkill()

  //アプリ起動時の応答
  .onLaunchRequest(async responseHelper => {
    //obnizを動かす
    await dispenser();
    responseHelper.setSimpleSpeech({
      lang: 'ja',
      type: 'PlainText',
      value: `${TEMPLATE_INQUIRY}`,
    });
  })
  
  // スキルの終了リクエスト
  .onSessionEndedRequest(responseHelper => {})
  .handle();

const clovaMiddleware = clova.Middleware({
  applicationId: clovaExtensionId,
});

app.post('/', clovaMiddleware, clovaSkillHandler);
```

Endpoint は赤枠で囲んだ部分 (endpoint) をクリックすると新しいウィンドウが開くので、https://＊＊＊＊＊.runkit.sh が URL です。(＊＊＊＊＊の部分は自分の情報です。「Cannot GET /」と表示されていますが、動作には影響ないので無視してください。)
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/0e40d0a1-e161-a964-1c55-9ed279447dad.png">
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/8ba7e529-6581-1449-b8fc-97e5d809da55.png">

### obniz Cloud API
Milkcocoa (MQTT) の部分は <A HREF="https://obniz.io/doc/obniz_api">obniz Cloud API</A> が用意されているので、何もする必要はありません。また、Wio Node の部分もサーボモーターを obniz へ繋ぎ替えるだけで、デバイスのコードを書く必要はありません。(素晴らしい！！)

## まとめ
コードをほとんど書かずに移植することができてしまいました。機会があればまたどこかのイベントでお披露目したいですね！

(追伸: 2018 年 12 月 6 日に開催された「<A HREF="https://rpacommunity.connpass.com/event/108003/">IT Girl's Talk〜Xmas Special〜【女性限定】</A>」でお披露目予定でしたが、深い闇に包まれてしまって出展できなかったので、関係者にはご迷惑お掛けしましたことを改めてお詫び申し上げます。)

(追伸: 2018 年 12 月 19 日 に開催された「<A HREF="https://vuidesigner.connpass.com/event/109950/">Voice UI/UX Designer Meetup #1</A>」でお披露目できました！
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/8cc20e09-34b5-a101-b8ca-05513dd770af.jpeg" width="45%"> <img src="https://qiita-image-store.s3.amazonaws.com/0/72479/105e6a8a-78e5-90e5-781f-45d8ab972184.jpeg" width="45%">