リモートワークで職場に荷物が届いたことをATOM LiteとAzure Logic Appsを使って通知させる

# 課題
**リモートワーク推奨**とは言え、物理的なモノのやり取りがまったく無くなる訳ではなく、書類・雑誌・荷物などが職場の自席に届いたり。そして、それをたまたまFaceToFaceの打合せをする為に職場に行ったときに見つけたり。
なんだかんだ言って、まだまだアナログなやり取りって必要な場合があると思っています。急に全部をデジタル化って言っても、相手があってのものなので、強制的に「全部メールで知らせてくれ！」とはなかなかいかなくて。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/88210bbe-8a37-5354-05d1-bca28d345d5d.png)

# 作ったもの
**荷物を置いたときに押してもらうボタンを設置して、自分に通知が来るシステム**

# システム構成
* ATOM Lite：押してもらうボタン
* Azure Logic Apps：HTTPリクエストからTeamsへの連携
* Teams：通知が来て、荷物が来たのが分かる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/19a1256d-7819-c9bc-0837-d6eb8856fe48.png)

# 通知結果
Microsoft Teamsにこんな感じで通知が届きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/0a7b4a7e-a98f-6dcd-5c4c-5242305753bb.png)

# Atom Lite側
* ボタンが押されたらHTTPリクエストを発行
* WiFi接続は各自の環境を使用する
* HTTPリクエストのURLは、Azure側の設定を使う

```cpp
#include "M5Atom.h"
#include <WiFi.h>
#include <HTTPClient.h>

#define JST 3600 * 9
#define COLOR_BLANK   0x000000 // black/blank
#define COLOR_WHITE   0xffffff // white
#define COLOR_RED     0xff0000 // red
#define COLOR_GREEN   0x00ff00 // green
#define COLOR_BLUE    0x0000ff // blue
#define COLOR_PURPLE  0xff00ff // purple
#define COLOR_EMERALDGREEN  0x00ff80 // emerald green

// ★★★★★設定項目★★★★★★★★★★
const char *ssid = "xxxxxxxx";
const char *password = "xxxxxxxx";
// ★★★★★★★★★★★★★★★★★★★

uint8_t DisBuff[2 + 5 * 5 * 3];

void setDisp(uint32_t color)
{
  DisBuff[0] = 0x05;
  DisBuff[1] = 0x05;
  for (int i = 0; i < 25; i++)
  {
    // modified color: M5Atom libraryのバグ?(RとGが逆)
    //    DisBuff[2 + i * 3 + 1] = color >> 16;
    //    DisBuff[2 + i * 3 + 0] = color >> 8;
    DisBuff[2 + i * 3 + 0] = color >> 16;
    DisBuff[2 + i * 3 + 1] = color >> 8;
    DisBuff[2 + i * 3 + 2] = color;
  }
  M5.dis.displaybuff(DisBuff);
}

void wifiConnect()
{
  Serial.print("Connecting to " + String(ssid));

  //WiFi接続開始
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  //接続を試みる(15秒)
  for (int i = 0; i < 30; i++)
  {
    if (WiFi.status() == WL_CONNECTED)
    {
      //接続に成功。IPアドレスを表示
      Serial.println();
      Serial.print("Connected! IP address: ");
      Serial.println(WiFi.localIP());
      break;
    }
    else
    {
      Serial.print(".");
      delay(500);
    }
  }

  // WiFiに接続出来ていない場合
  if (WiFi.status() != WL_CONNECTED)
  {
    Serial.println("");
    Serial.println("Failed, Wifi connecting error");
  }
}

void wifiDisconnect()
{
  Serial.println("Disconnecting WiFi...");
  WiFi.disconnect(true); // disconnect & WiFi power off
}

// Teamsへの通知
int httpTrigger(void)
{

  int res = 0;
  HTTPClient http;
  String url = "https://xxxxxxxxxx";
  //Serial.println("connect url: " + url);

  Serial.print("[HTTP] begin...\n");
  if (http.begin(url))
  {
    Serial.print("[HTTP] GET...\n");
    // start connection and send HTTP header
    int httpCode = http.GET();

    // httpCode will be negative on error
    if (httpCode > 0)
    {
      // HTTP header has been send and Server response header has been handled
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);
      //Serial.println(http.getSize());

      // file found at server
      String payload;
      if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_ACCEPTED || httpCode == HTTP_CODE_MOVED_PERMANENTLY)
      {
        payload = http.getString();
        Serial.println("HTTP_CODE_OK");
        Serial.println(payload);
      }
    }
    else
    {
      Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
      // コネクションエラーが発生しているので、一旦WiFiを切断
      wifiDisconnect();
      res = HTTPC_ERROR_CONNECTION_REFUSED;
    }
    http.end();
  }
  else
  {
    Serial.printf("[HTTP] Unable to connect\n");
    res = HTTPC_ERROR_NOT_CONNECTED;
  }
  return res;
}

void setup()
{
  M5.begin(true, false, true);
  delay(10);
  setDisp(COLOR_WHITE);

  // WiFi接続
  wifiConnect();
  setDisp(COLOR_BLANK);

  // NTP同期
  configTime(JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");
}

void loop()
{
  // STAモードで接続出来ていない場合
  if (WiFi.status() != WL_CONNECTED)
  {
    wifiConnect();
  }

  if (M5.Btn.wasPressed())
  {
    setDisp(COLOR_GREEN);
    httpTrigger();
    setDisp(COLOR_BLANK);
  }

  delay(50);
  M5.update();
}

```




# Azure Logic Appsの設定
1. トリガーは[HTTP要求の受信時]を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/71fa57ed-0e75-11a0-b4b1-db591db640ca.png)

1. 保存するとURLが発行されるので、このURLをAtom Lite側に実装します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/f579d71e-3009-eff4-0ed5-f5c2ae6b1f93.png)

1. アクションは[HTTP Webhook]を選択。URIはTeamsの[Incoming Webhook]を使うので、Teamsの項目を参照。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/038f58db-413d-1fe7-8823-b59106b8f8cc.png)

1. [サブスクライブ - 本文]は、以下のように設定。フォーマットが決まっているので、ただ文字を入れると拒否されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/50f0d31a-bba4-e63e-0450-694c4f03d7dc.png)

# Teams側の設定
1. 通知させたいチャンネルの詳細で[コネクタ]を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/9a71f71d-2f67-7e20-774d-f48b6a29bbd1.png)

1. [Incoming Webhook]を選択して、構成を作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/b9f00250-7459-f4dc-293e-59991cceca36.png)

1. ここで生成される[URL]をAzure Logic Appsの[HTTP Webhook]に設定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/7bf88360-27d2-ca74-4c01-f171b3f6094f.png)

# 注意事項
Azure Logic Appsの場合は、Teamsへの[メッセージ送信]の機能もありますが、自分のアカウントでサインインした場合はTeamsでの通知が機能しなかったので、今回はTeamsの[Incoming Webhook]を使用しています。自分自身の投稿として認識されるからかなと思いながら、尚且つ、メンションの付け方も不明だったので、[Incoming Webhook]を使用しています。

# まとめ
ハードウェアが絡むと開発が少し面倒でお金も掛かる気がしますが、Atom Liteを上手く使えば安価にいろいろ作れると思います。監視カメラで自席チェックなどもやってますが、定期的に自分で見に行ったりする必要がないので、全然こっちのほうが便利です。