MQTTブローカーの shiftr.io で HTTPリクエスト（内向き/外向き）を組み合わせる【2022年4月版】

MQTT を利用する際の仲介役となる MQTTブローカーを準備する際、外部サービスやライブラリ等を利用して準備するなどしますが、クラウド・デスクトップの両方で使える MQTTブローカーで shiftr.io というサービスがあります。
この shiftr.io は、MQTT だけでなく HTTP のインタフェースを持っており、HTTPリクエストと MQTT の Pub/Sub との間をつなぐ役割を担うこともできます。

その shiftr.io の HTTPインタフェースについては、過去に一部を試したことがあり、手順を以下の記事に書いていました。

●【NoCode 2020】 MQTTブローカーの shiftr.io と IFTTT の Webhook をつなぐ（shiftr.io の HTTP Interface） - Qiita
　https://qiita.com/youtoy/items/c690e2f1aa064e6dd289

しかし、その時に試せてなかった機能もあったり、試したものの記事にしてなかった部分もあったため、あらためて今回の記事で情報をまとめてみようと思います。

## shiftr.io の概要
冒頭で書いたとおり、shiftr.io は MQTTブローカーのサービスで、以下のクラウド版とデスクトップ版とが提供されています。

- クラウド版： https://www.shiftr.io/cloud/
- デスクトップ版： https://www.shiftr.io/desktop/

### クラウド版
クラウド版は、以下の Pricing のページに書かれているように、有償版・無償版があります。

●Cloud - shiftr.io 【Pricing】
　https://www.shiftr.io/cloud#pricing
![Pricingのページ（shiftr.io）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/ddc1113f-b603-1189-b143-a31757f624f2.jpeg)

それらの差は、利用時のコネクション数の上限や 1秒あたりに送信可能なメッセージ数です。それともう1つ、無償版のインスタンスは 1日に稼働させられる時間に制約があります。
この無償版は、稼働させ続ける運用は難しいですが、実験用に利用するのにはとても便利です。

それと、無償で使えるものには、（この後でも使ってみますが）パブリックなインスタンスで作られたパブリックな MQTTブローカーがあります。こちらに関する補足は、後で記載します。

### デスクトップ版
shiftr.io のデスクトップ版は、GUI版と CLI版とがあります。
自分が使っているのが GUI版のみであるため、この記事では GUI版のほうだけを扱います（この後、デスクトップ版と記載している部分は、GUI版のほうの話になります）。

デスクトップ版は、アカウントを作成して利用する形のクラウド版と異なり、アカウント作成をすることなく利用できます。また、ローカルでしかつなげられないですが、無償で利用できます。
自分は、自宅内の異なる機器間で MQTT を使った通信をさせたい場合などに、これを利用することがあります。

### 使い方
この記事では、細かな使い方の説明は省略します。
そのあたりは、「shiftr.io 使い方」などでキーワード検索して調べてみてください。

## shiftr.io で HTTPリクエストが扱える機能
### 外から shiftr.io へアクセスする
まずは、shiftr.io に対して外から HTTPリクエストを送る話を書きます。これは、冒頭で紹介していた記事でも扱った機能で、公式ドキュメントの以下で説明されています。

●HTTP Interface - shiftr.io
　https://www.shiftr.io/docs/broker/http-interface

それと、自分はこれまで試したことがなかったのですが、デスクトップ版でも利用できます。

[shiftr.io の HTTP Interface のページ](https://www.shiftr.io/docs/broker/http-interface) の「Ports」・「Endpoint」のそれぞれの項目の部分で、仕様が書かれています。おおまかには、以下の通りです。

- クラウド版
   - URL は `https://INSTANCE_NAME:TOKEN_SECRET@INSTANCE_DOMAIN` （INSTANCE_NAME、TOKEN_SECRET、INSTANCE_DOMAIN は、shiftr.io のアカウント作成後に、インスタンスの作成等を行って取得する情報）
   - ポートは `443`
- デスクトップ版
   - URL は `http://IP_OR_DOMAIN` （※ デスクトップ版は https は利用不可）
   - ポートは `1884`

また、メッセージの取得方法などは以下のとおりです。

- 最新のメッセージを取得（GETリクエスト）
   - トピック /foo/bar に関して最新のメッセージを取得 ⇒ `ENDPOINT/broker/foo/bar` （curl コマンドの例： `$ curl 'ENDPOINT/broker/foo/bar'`） 
- パブリッシュ（POSTリクエスト）
   - トピック /foo/bar でパブリッシュ ⇒ curl コマンドの例： `$ curl -X POST 'ENDPOINT/broker/foo/bar' -d 'Hello World!'`

#### クラウド版（パブリックインスタンス）でのお試し
次に、クラウド版で試してみます。

誰かに見られても問題ないテスト用の情報を使い、単純に試すだけという場合などは、誰でも接続可能な状態になっているパブリックインスタンスを利用することも可能です。これを用いることで、アカウント作成などの一部の手間を省くことができます。

今回は、これを用いてクラウド版のお試しを進めていきます。
パブリックインスタンスによるパブリックな MQTTブローカーを用いる場合、[shiftr.io のページの「Try」というメニュー](https://www.shiftr.io/try)」 を選ぶと、接続用の情報を確認できます。
![パブリック版.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/659570fe-c37f-1fca-e9c0-e83ba0567b8c.jpeg)

`mqtt://public:public@public.cloud.shiftr.io` という情報が書かれていますが、これは上で出てきた `ENDPOINT/broker/foo/bar` の中の `ENDPOINT` に該当する部分です。また、上で `ENDPOINT` をもっと細かく書いた `https://INSTANCE_NAME:TOKEN_SECRET@INSTANCE_DOMAIN` というものがありましたが、こちらでいうと、以下の対応関係になります。

- INSTANCE_NAME　⇒ public
- TOKEN_SECRET　⇒ public
- INSTANCE_DOMAIN　⇒ public.cloud.shiftr.io

それでは、Node.js＋MQTT.js、および curl を使ったお試しをやってみます。
Node.js＋MQTT.js の組み合わせは、MQTTクライアントを準備するために用います。そして、curl は HTTP Interface を利用するために用います。

適当なフォルダで `npm install mqtt` を実行し、その後に以下の JavaScript のプログラムを用意します。プログラムは、[shiftr.io の「Try」のページ](https://www.shiftr.io/try)」 の下のほうに掲載されたプログラムを元に作成しました（一部を削ってシンプルにしています）。

```javascript
const mqtt = require("mqtt");

const client = mqtt.connect("mqtt://public:public@public.cloud.shiftr.io", {
  clientId: "javascript",
});

client.on("connect", function () {
  console.log("connected!");

  client.subscribe("hello/test");
});

client.on("message", function (topic, message) {
  console.log(topic + ": " + message.toString());
});
```

このプログラムの動作は、トピック「hello/test」でサブスクライブする設定をし、メッセージを受けとった際にその内容（トピックとメッセージの内容）をログで出力するというものです。
上記のプログラムを nodeコマンドで実行すれば、パブリックな MQTTブローカーに接続され、サブスクライブ・メッセージのログ出力が行われる状態になります。

MQTTブローカーへのパブリッシュは、以下のコマンド（HTTP Interface を用いるやり方）で実行します。トピック「hello/test」・メッセージの内容「テストメッセージです！」という内容を送りました。

```bash
$ curl -X POST "https://public:public@public.cloud.shiftr.io/broker/hello/test" -d "テストメッセージです！"
```

そうすると MQTTクライアント側で、以下のログが表示されると思いますので、確認してみてください。

![MQTTクライアント側](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/4476608c-7c1e-9a8f-e60a-f39152c895e1.png)

さらに、上記のメッセージのやりとりをした後、HTTP Interface を用いた最新のメッセージの取得も試してみましょう。
以下にコマンドと、そのコマンドを実行した時の結果を掲載します。

```bash
$  curl https://public:public@public.cloud.shiftr.io/broker/hello/test
```

![最新のメッセージの取得](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/4eb95a39-9fef-1e34-4abf-70a9f3206a3c.png)

無事に、パブリックな MQTTブローカーで、MQTT（サブスクライブ）と HTTP Interface を併用した通信が行えることを確認できました。

#### デスクトップ版でのお試し
今度は、デスクトップ版で試してみます。

自分は　Mac を使っているので、Mac用の GUI版MQTTブローカーをダウンロードして起動しました。以下が、起動した直後の画面です。
![デスクトップ版の画面.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/86e8d171-0ac1-4473-b5cb-0e612d982f40.jpeg)

MQTTクライアントは、先ほどクラウド版のお試しで使ったものを、少し書きかえて使います。
具体的には以下になるのですが、上で使ったものからの変更点は　MQTTブローカーを指定する部分くらいです。

```javascript
const mqtt = require("mqtt");

const client = mqtt.connect("mqtt://localhost:1883", {
  clientId: "javascript",
});

client.on("connect", function () {
  console.log("connected!");

  client.subscribe("hello/test");
});

client.on("message", function (topic, message) {
  console.log(topic + ": " + message.toString());
});
```

デスクトップ版の MQTTブローカーに `mqtt://localhost:1883` という指定をして接続しています。この時に指定するポート番号は、HTTP Interface で用いる `1884` でなく、MQTT用の `1883` になるのでご注意ください。

次に、curl を使ってメッセージをパブリッシュしてみます。
以下のコマンドで、トピック「hello/test」・メッセージの内容「テストメッセージです！」という内容を送ります。

```bash
$ curl -X POST "http://localhost:1884/broker/hello/test" -d "テストメッセージです！"
```

コマンドの実行後に、クライアント側のログ出力を見てみると、以下のようにトピック・メッセージの内容が表示されているのが確認できました。
![デスクトップ版MQTTブローカーからのサブスクライブ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/3eb41509-0c9a-0f1f-a1b6-6b1616cb632d.png)

また、最新のメッセージの内容を curl で取得してみます。コマンドと、実行した結果は以下のとおりです。

```bash
$ curl "http://localhost:1884/broker/hello/test"
```

![デスクトップ版MQTTブローカーから最新のメッセージを取得](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/a76d2402-5b39-1281-3f94-468f48386803.png)

デスクトップ版でも無事に動作確認ができました。

### shiftr.io から外にアクセスする
今度は、shiftr.io から外部に HTTPリクエストを送る機能の話です。
こちらは、クラウド版のアカウントが必要になるので、アカウントを作成してログインしてください。

そして、インスタンスを作成して設定画面を開きましょう（※ 設定画面は、画面右下にある歯車のアイコンから開くことができます）。そうすると、左側に「Webhooks」というメニューがあります。
![クラウド版の設定.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/2f8b04bf-bd5d-db23-e384-80342b96b69c.jpeg)

そこで、「Create Webhook」というボタンを押すと以下の画面が表示されます。ここで、Webhook の各種設定を行います。
![Webhooksの作成.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/e80420e7-8b16-edff-a04f-43b6362ef1db.jpeg)

この画面を見た限りでは、ヘッダに認証情報を埋め込むような仕様のサービスには用いることはできないようです（自分が書いた Qiitaの記事に登場するものだと、LINE Notify などが該当します）。

そうでないパターンのもので、例えば IFTTT との組み合わせは問題なく行えそうです。試しに、「IFTTT 経由で、スマホ用の IFTTT アプリに通知を送る」という処理を行ってみます。

#### IFTTT側の準備
IFTTT側の設定は、以下のとおりです。

![IFTTTの内容.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/2f01d8f3-bf9b-591f-0967-ef8e05bd7985.jpeg)

![IFTTTの設定2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f475c90e-8c5c-e6a5-c149-9d4833c3b3bd.jpeg)

- 設定に関する補足
   - Webhook と Notification の組み合わせ
   - Webhook のイベント名： 「webhook_test」
   - Notification　のメッセージ本文は「{{Value1}}」 ← Webhook で送られてきた内容を利用 

上記のイベント名は、適当に決めたものです。

#### shiftr.ioでの Webhook の設定
shiftr.io側の Webhook の設定は以下としました。
![shiftr.ioでの Webhook の設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/4fe34aca-8644-c112-0109-34c5cc8c11d8.jpeg)

ポイントになりそうな部分は、以下のとおりです。

- Topic（Webhook と紐付けるトピック）： `webhook/test`
- URL： 【IFTTT の Webhook用のURL（キーを含む URL）】
- Body： `{"value1":"shiftr.ioからの通知"}`
- Content Type： `application/json`

上記のトピックは適当に決めたものです。

また、Body に設定した JSON に関しては、IFTTT側で「Notification　のメッセージ本文に `Value1` のみを利用する形」にしていることから、 `value1` のみをい用いてメッセージ用のテキストを設定しています。

#### 動作確認
あとは、 `webhook/test` というトピックで MQTTブローカーにメッセージを送れば、スマホ用の IFTTTアプリに通知が来るはずです。

まずは、HTTP Interface を使ったメッセージ送信を行い、動作確認を行います。

```bash
$ curl -X POST "https://INSTANCE_NAME:TOKEN_SECRET@INSTANCE_DOMAIN/broker/webhook/test" -d "curlからのテスト"
```

※ `INSTANCE_NAME:TOKEN_SECRET@INSTANCE_DOMAIN` の部分は、ご自身のものに置きかえてください

上記のコマンドを実行すると、「shiftr.io の HTTP Interface を用いたメッセージング ⇒ shiftr.io の Webhook（送信） ⇒ IFTTT の Webhook（受信） ⇒ IFTTT の Notification」という処理が実行され、スマホの IFTTT アプリに以下のメッセージが届きます。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">MQTTブローカーの <a href="https://t.co/8lBLAbWjRS">https://t.co/8lBLAbWjRS</a> の Webhook（HTTP での外向きのアクセス）を使い、IFTTT と連携させてみるテスト。<a href="https://t.co/8lBLAbWjRS">https://t.co/8lBLAbWjRS</a> にメッセージを送る部分も、通常の MQTT のパブリッシュではなく、HTTP Interface（HTTP での内向きのアクセス）を使っています。 <a href="https://t.co/wHuYZm6xCR">pic.twitter.com/wHuYZm6xCR</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1513538129604788229?ref_src=twsrc%5Etfw">April 11, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

MQTT のメッセージとしては「curlからのテスト」という内容を送っていますが、スマホアプリで受信する内容は「shiftr.io の Webhook」の「Body」で設定した内容が使われるます。そのため、スマホの IFTTTアプリの通知では、「shiftr.ioからの通知」というメッセージ本文が表示されています。

もう 1つ、MQTT.js を用いたパブリッシュ（Node.js のプログラムを利用）を使った方法も試してみます。用いるプログラムは以下のとおりです。

```javascript
const mqtt = require("mqtt");

const client = mqtt.connect("https://INSTANCE_NAME:TOKEN_SECRET@INSTANCE_DOMAIN/",{
    clientId: "javascript",
});

client.on("connect", function () {
  console.log("connected!");

  client.publish("webhook/test", "MQTT.jsを用いたパブリッシュ");
});
```

※ `INSTANCE_NAME:TOKEN_SECRET@INSTANCE_DOMAIN` の部分は、ご自身のものに置きかえてください

上記のプログラムを nodeコマンドで実行したところ、スマホ用の IFTTTアプリに通知が来るのが確認できました。

## おわりに
shiftr.io の HTTP Interface と Webhook のそれぞれ試し、これらを用いた仕組みを動作させることができました。便利に使えそうな機能なので、引き続き、お試しをやっていければと思います。