MESH で倉庫の出入りを確認&通知しよう

目次

はじめに

運用上、倉庫の出入りを管理したいことがあります。
○分前に人の出入りがあったか?を確認しようとすると、設置した監視カメラの映像を使うことが一般的かもしれませんが、確認に時間がかかります。
そんなとき、入退室の時間を何かのデータベースに登録しておき、その情報を簡単に確認できるとうれしいです。

そこで、kintone と、MESH の人感ブロック・LED ブロック・ボタンブロックを組み合わせて、倉庫の出入り状況を確認できるしくみを作ってみました。

MESH については MESH 公式サイト (External link) を確認してください。

完成イメージ

このカスタマイズでは次のことを行います。

  • MESH の人感ブロックを使って、「エリア内に人がいるか?」をチェックします。チェック結果は kintone のアプリに登録します。
  • MESH のボタンを押して、直近 5 分以内に人がいたか(該当するレコードがあるか)を kintone に問い合わせします。
    • 人がいた場合:MESH の LED ブロックは赤に点灯します。
    • 人がいなかった場合:MESH の LED ブロックは緑に点灯します。

MESH と MESH を接続しているモバイルは Bluetooth の範囲内としてください。

下準備

kintone の設定

下記手順で作成する kintone アプリの ID と API トークンは、 MESH の設定で利用するので控えておいてください。

アプリ ID は、アプリの URL で確認できます。
例:URL が https://sample.cybozu.com/k/10/ の場合、「10」がアプリ ID です。

kintone アプリの作成

次のフィールドをフォームに設置します。

フィールド名 フィールドタイプ フィールドコード 初期値 備考
日時 日時 DateTime レコード登録時の時刻を初期値にする 人感センサで人を感知した時間です。
ステータス 数値 Status 人感センサの値です。
ただし登録するのみで、プログラム内で利用はしません。

API トークンの生成

「レコード閲覧」「レコード追加」権限のある API トークンを生成します。

API トークンの生成方法は「 API トークンを生成する (External link) 」を参照してください。

MESH の設定

MESH SDK

MESH SDK は、MESH アプリのレシピで利用するカスタムブロック(ソフトウェアブロック)を作成するツールです。
カスタムブロックでは、JavaScript を使って、複雑な処理やさまざまな Web サービスとの連携ができます。

以下の手順にしたがって、MESH のカスタムブロックを 2 つ作成します。

作成するカスタムブロックは以下の「入退室(登録)ブロック」「入退室(取得)ブロック」です。

  • 入退室(登録)ブロック:人感センサのチェック結果を kintone に登録するブロックです。
  • 入退室(取得)ブロック:kintone に問い合わせして LED ブロックを点灯させるブロック
  1. [Create New Block]をクリックし、新規ブロックを作成します。

  2. [Import]タブを選択します。

  3. エディタ部分に、後述する「 入退室(登録)」および「 入退室(取得)」の JSON の内容をそれぞれ貼り付けます。
    [Load JSON]をクリックすると、上書きの確認ダイアログが表示されるので[OK]ボタンをクリックし、インポートします。

  4. インポート後、下記 Property の Dafault Value を書き換えます。

    プロパティ名
    ドメイン kintone のドメインを指定してください
    アプリケーションID kintone アプリの作成 で作成したアプリのアプリケーション ID
    API トークン API トークンの生成 で発行した API トークン

    Property の Default Value は、レシピにブロックを配置する際、初期値として設定されます。
    Property の値は、MESH アプリのレシピからも変更できます。

  5. [Save]をクリックし、保存します。

MESH アプリ

MESH アプリでは、Bluetooth で接続した MESH ブロックや MESH SDK で作成したカスタムブロックを「レシピ」のキャンバス上に配置し、処理フローを作成します。

  1. [+新しいレシピ]をタップし、レシピを作成します。

  2. MESH ブロックを接続します。

    • 人感ブロック
    • ボタンブロック
    • LED ブロック 2 つ。

    MESH ブロックの接続

  3. 接続した MESH タグを、ドラッグ&ドロップでキャンバスに配置し、設定します。

    ブロック 設定
    人感センサ
    • 感知したら
    • 間隔(秒):60
    ボタンブロック
    • 1 回押されたら
    LED ブロック 1 つめのブロック
    • 点滅する
    • 色:オレンジ
    • 明るさ:1
    • 時間(秒):5 秒
    • 周期(秒):1 秒
    2 つめのブロック
    • 点滅する
    • 色:ミント
    • 明るさ:1
    • 時間(秒):5 秒
    • 周期(秒):1 秒
  4. MESH SDK にサインインします。

    MESH SDK のサインイン

  5. カスタムの[追加]をタップし、MESH SDK で作った 2 つのカスタムブロックを追加します。

    • 入退室(登録)ブロック
    • 入退室(取得)ブロック

    カスタムブロックの追加

  6. 追加したカスタムブロックを、ドラッグ&ドロップでキャンバスに配置します。
    MESH SDK でブロックを作成したときの Default Value が初期値に設定されます。
    変更する場合は、対象のブロックをタップして表示される画面で変更してください。

  7. 配置したブロックをそれぞれ次のように接続します。
    「入退室(取得)」ブロックの出力コネクタについては、次のように接続してください。

    • 1 つめのコネクタ(confusion):色をオレンジに設定した LED ブロック
    • 2 つめのコネクタ(vacant):色をミントに設定した LED ブロック

動作確認

  • 人感ブロックの前に立って感知されたら、kintone にレコードが登録されます。
  • ボタンブロックのボタンを押して、LED ブロックが光ります。

サンプルコード

入退室(登録)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{
  "formatVersion": "1.0",
  "tagData": {
    "name": "入退室(登録)",
    "icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAADc0lEQVRoQ+2a3VEbMRCAd3UugFQAVIBTQUwHoQOoIPLp3jHv1nFUEFIBJWAqwB3EHcR5t24z65E85+MMuh+JhIlm/OCxfvbTrla7KyN8kIYfhAP+gwyhSSnlRAjxBREnAHBiPzz1GgCWiLjcbDY/iqJYvrXeu2iEAZIkuQYABvBpC2PMTVEUi0Odo4JIKY+EENeIKH2kr/dBxGKz2TAQa2yveYNIKcdCiG88uixLnmzVRhgp5cloNHogonGbcQ0wbG4X9fW9QbIsmxDRI09sjDl/Tc31xVkTo9HosS+Em9eeHZZhp5koIGma3nY1p4NnArGYz+fTHZyvmrtqpDrOdy3fflXLCK4RpRSbo6938mVw/RZa63P+EhSEHUSSJM9tpWvT32klKMh0Oi2cp2sjXJu+RHST5/lsD8S62Eb3KIQ4JqKZXeSOiBpv27Isl+4mDmxWe+a1B5Km6QwR+cbt3NwO8QRKqV8AcNR5Mr+BK631aWgQ8pOlXy+tNdZNi3evcQeFEBNE/G6XvDDGHArk1u6iUkq9D8hr+9LlHlFK/axEtP22/fDotdb6U1Cv9W6HfWiNZFk2I6JezsNDjXdaaxlUIyHDEwdojPnM7j4oiHXB/36IwiAhtdIpaLTZ3VcWrizLRZvESilVAMA2KRuwbc+Gm8/btPoIwJuQJAmbWK/ssCLD0mqjfWLVB4THcqqbJMnDADAM0T3V7QtiYVgzHHh2NbM7Y8ysV/FhCBA3h3UArcpBiHgzn8//jnJQfTNs4nUJAGfW5FycxxWaFSI+AcDiNYCoh72qCQC4JaIVET1x7sICH/KA9lyNiYirkGdckTTGXDVVcKJ4LQfyRsZYr5Ox8C8aEd3neX5V/yEqSJZlzwPUttbGmNP6gY8GYs2Ew/reralAGA0kTdPLSmLWF2ZXBop+2JVSfBluQ5wB2gvziqaRoQsRRHSV5/l9VI0EioD3zCuKRkIV6owxn5z3igIykNttulOmeZ5ziuBf++16QId0uw0ytC9idwUZ2O2+EMOZV3DTGtjtHjSvGCCh679b8woKEsjtNppXUJBQbrdOwi8AQUEi1X6ZaxEUxD0c2b9ouCywqwOsj+Ok7IkfnLg8FRSkvrJ9bx+XZTlGRE6cju0zRvV/KG4Yl3q2HxYWEX9boXcvYtX5o4IMpYqmeT4MyB+14EAP6Tq37QAAAABJRU5ErkJggg==",
    "description": "",
    "functions": [
      {
        "id": "function_0",
        "name": "postKintoneApp",
        "connector": { "inputs": [{ "label": "motion" }], "outputs": [] },
        "properties": [
          {
            "name": "ドメイン",
            "referenceName": "domain",
            "type": "string",
            "defaultValue": "example.cybozu.com"
          },
          {
            "name": "アプリケーションID",
            "referenceName": "appId",
            "type": "number",
            "defaultValue": "1"
          },
          {
            "name": "API トークン",
            "referenceName": "apiToken",
            "type": "string",
            "defaultValue": "TOKEN"
          }
        ],
        "extension": {
          "initialize": "",
          "receive": "",
          "execute": "/*\n * MESH program\n * Copyright (c) 2019 Cybozu\n *\n * Licensed under the MIT License\n */\nconst url = `https://${properties.domain}/k/v1/record.json`;\nconst data = {\n  app: properties.appId,\n  record: {\n    DateTime: { value: new Date() },\n    Status: { value: messageValues.stateValue }\n  }\n};\n\najax ({\n  url: url,\n  data: JSON.stringify(data),\n  type: 'POST',\n  contentType: 'application/json',\n  dataType: 'json',\n  timeout: 5000,\n  headers: { 'X-Cybozu-API-Token': properties.apiToken },\n  success: (contents) => {\n    log('success');\n  },\n  error: (request, errorMessage ) => {\n    log('error');\n    log(request.responseText);\n    log(errorMessage);\n  }\n});\n\nreturn {\n  resultType: 'pause'\n};\n",
          "result": ""
        }
      }
    ]
  }
}

入退室(取得)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
{
  "formatVersion": "1.0",
  "tagData": {
    "name": "入退室(取得)",
    "icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAD9klEQVRoQ+1a0VHbQBC9PbkAqCBQAVBBoINQQaCCyD79A9/4hKggpALoAFMB7iBOBXH+ddrMc04eWZbkO1sSDJOb8fDh9e2+3ber3RUkPsihD4JD/AfSRiTDMDyVUn4molMhxIH94Oq5EGJKRNM0TX8kSTLdpO9NIgIAQRBcCSEAwOVMjDE3SZJM6oSdgIRhuGe9hb9zFw9VKcQ9UsorIgpdrC/LEFGSpikAIWIrpxHIaDS6IKKvFZ7DRU/WSzMXo8IwPBgMBo/MfOwiX+v5f3Q7T5JkRW8lECgNguC7Q+hnxpiz8qVlIxCJwWDwvCuI/F6bO9C7jMwakBqlE2aeEdEejCEiJGZ+JlrrsyYvj0aju23p1BCZZDweD5fgarh8HAQBPDiXUl6Ox+NlkgFoEASPxWgZY/areIu7oyg6ZebnXehU91vLhoVttTkCekGgijZKqS9CCIDJz4HW+leVQqUUQLhWJ1+8SzY4Va3y7bYIIIcWR2tdl2uI7KuvdT7yeVS8gYRhCOMQjUXEmPkmjuPrKuXD4TCRUn7zMcxXNtfvBUQpBaNgNJ4njSDwfce0Wik2zkCiKLpi5tzzc2YexnH80ORBpdTvHLSvpz3kZ1rrQycgpZyYW15u7H+UUuxh0NaiyFEnIEWK2EgkLlrfK5BFchtjLpuatyJApdTPQkfrgn0bmbnWet8pItvc/m6TfRswURRdMzPa9S7PvdY6dIqIUgoPvwshxExKeX57e7sx0WF5l+1J7hljzAnGio1AKtqRjU1iKU/eR4tSbkcwgmqtT1y50mVUnJrG3FA71b0WWvdzrfWTKxCb9CjXbbcqi9zI7dhIrVwQnk3TdFrXrjcBs60/KLbTdFjQMbXRqB+sfDztI2unTjSbu4IBCLdR18dAH1kbGfRr29Ls3hhz7b188DHSR9YWAK91EBHdFCfVsj7nHPEx1FXWzjZ4Ph1Zyi3GAzyv8CGiFyHEpAmAd7K7Gtckh0gIIe6wyGDmlyzL8GCd1W1hbF4dMzP6vCNsJOt6vV4jsmFiLO/HipuapX+Y+SGO48s3pVYURa8t7LYwDx2WE763iFiaoK3f+RSf6L3nSEWrswugtX6vt4gopfAwxD6sjbNGrz6BtLqIYObL4vKjFyAddcAr9OoFSFeLuuLOuRcgLZXdtdwqbnQ6B9Jm2a2oErstsX3KTstld011Tq/OI9Jy2a2lVx9AWi27dfTqFEhHZbeSXp0C6arslpHgHUmnQHra/QLXpFMgmACllHgLjIEqnwJ9il6TLIayF2aeZlnWLZCyFfbV93GWZfkr7k/2RVDx/1Dyn2HVs/jAWCL6Y43GSmptZdtpRNpyvcs9HwbIXwb2Gg9ECfJDAAAAAElFTkSuQmCC",
    "description": "",
    "functions": [
      {
        "id": "function_0",
        "name": "getKintoneApp",
        "connector": {
          "inputs": [{ "label": "button" }],
          "outputs": [{ "label": "confusion" }, { "label": "vacant" }]
        },
        "properties": [
          {
            "name": "ドメイン",
            "referenceName": "domain",
            "type": "string",
            "defaultValue": "example.cybozu.com"
          },
          {
            "name": "アプリケーションID",
            "referenceName": "appId",
            "type": "number",
            "defaultValue": "1"
          },
          {
            "name": "APIトークン",
            "referenceName": "apiToken",
            "type": "string",
            "defaultValue": "TOKEN"
          },
          {
            "name": "間隔(分)",
            "referenceName": "interval",
            "type": "number",
            "defaultValue": "5"
          }
        ],
        "extension": {
          "initialize": "",
          "receive": "",
          "execute": "/*\n * MESH sample program\n * Copyright (c) 2019 Cybozu\n *\n * Licensed under the MIT License\n */\n\nconst query = 'order by 作成日時 desc limit 1';\nconst url = `https://${properties.domain}/k/v1/records.json?app=${properties.appId}&query=${encodeURIComponent(query)}`;\n\najax({\n  url: url,\n  type: 'GET',\n  dataType: 'json',\n  timeout: 5000,\n  headers: {\n    'X-Cybozu-API-Token': properties.apiToken\n  \t},\n  success: (contents) => {\n    log('get: success');\n    const records = contents.records;\n    let outputIndex = 1;\n    if(records.length > 0) {\n      const current = new Date();\n      const latest = new Date(records[0].DateTime.value);\n      const diff = current.getTime() - latest.getTime();\n      if(diff < (properties.interval * 60 *1000)) {\n        outputIndex = 0;\n      }\n    }\n    runtimeValues.outputIndex = outputIndex;\n    callbackSuccess({\n      resultType : 'continue',\n      runtimeValues : runtimeValues\n    });\n  },\n  error: (request, errorMessage) => {\n    log('get: error');\n    log(request.responseText);\n    log(errorMessage);\n    callbackSuccess({\n      resultType : 'continue',\n      runtimeValues : runtimeValues\n    });\n  }\n});\n\nreturn {\n    resultType : 'pause'\n};\n",
          "result": "return {\n  indexes: [ runtimeValues.outputIndex ],\n  resultType : 'continue'\n};"
        }
      }
    ]
  }
}

サンプルコードの解説

今回作成した MESH カスタムブロックのポイントを解説します。
MESH SDK の詳細は、 MESH SDK リファレンスマニュアル (External link) を参照してください。

入退室(登録)ブロック

人感センサの結果を kintone に登録する

人感センサブロックは、人を感知したときに次のブロックへ信号を伝えるように設定しています。

Code
  • Execute メソッド
    人感センサブロックから伝わる結果は messageValues.stateValue に格納されています。
    この値を kintone に登録します。

入退室(取得)ブロック

レコードの取得結果に応じて出力先を変える

「5 分以内に人がいたか?」に応じて LED の色を変更させるため、2 つの LED ブロックを配置しています。
カスタムブロックで、レコードがなければ 1 つめのコネクタに接続したブロックへ信号を送り、レコードがあれば 2 つめのコネクタに接続したブロックへ信号を送っています。

出力コネクタを分岐させるには、次のように設定します。

Connector
  • 2 つの LED ブロックに出力するので、Output Connector を 2 つ用意します。

Code
  • Initialize メソッド
    Function 内で共有できる変数 runtimeValues を定義します。

  • Execute メソッド
    runtimeValues のプロパティに、出力先コネクタの番号を指定します。
    1 つめのコネクタのコネクタ番号は 0、2 つめのコネクタのコネクタ番号は 1 です。
    デフォルトでは、2 つめのコネクタに出力するようにしています。
    所定の時間以内のレコードがなければ、1 つめのコネクタに出力します。

    処理が終わったら callbackSucces() メソッドで Result メソッドに渡します。

  • Result メソッド
    Execute メソッドから渡された runtimeValues の値を indexes プロパティに設定することで、Output Connector に出力します。

おわりに

今回の記事では、「5 分以内に」人がいるか?をチェックしました。
チェックタイミングを「○時までに」とすると、指定時刻までに入室(または退室)が必要な業務において、入退室を管理できます。

また、cybozu developer network では、他にも kintone と MESH を連携した記事を紹介しています。
ぜひこれらの記事も参照してみてください。

information

この Tips は、2019 年 6 月版 kintone で動作を確認しています。